Giải quyết bài toán Partition Problem là một trong những thách thức thú vị trong lĩnh vực lập trình và thuật toán. Thuật toán này liên quan đến việc phân chia một tập hợp các số nguyên thành hai tập con sao cho tổng của các phần tử trong mỗi tập con bằng nhau.
Bản chất của vấn đề
Partition Problem là một dạng của bài toán NP-complete nổi tiếng. Nói cách khác, không có thuật toán thời gian đa thức nào giải quyết được tất cả các trường hợp của bài toán này, nhưng vẫn có nhiều phương pháp và chiến lược có thể được áp dụng để tìm ra lời giải tối ưu cho những tình huống cụ thể.
Cách tiếp cận sử dụng quy hoạch động
Một trong những cách hiệu quả nhất để giải quyết bài toán này là sử dụng quy hoạch động. Ý tưởng chính của phương pháp này là xây dựng bảng các giá trị tổng hợp, nơi mỗi giá trị trong bảng biểu thị khả năng đạt được tổng cụ thể với một tập hợp con của các số đầu vào.
Bước 1: Xác định điều kiện và khởi tạo bảng
Trước hết, tính tổng của tất cả các phần tử trong tập hợp. Nếu tổng này là số lẻ, bài toán không có lời giải vì hai tập con không thể cùng có tổng lẻ. Ngược lại, chia tổng cho 2 (gọi là sum/2
) để xác định mục tiêu của mỗi tập con.
def can_partition(nums):
total_sum = sum(nums)
if total_sum % 2 != 0:
return False
target = total_sum // 2
n = len(nums)
dp = [[False] * (target + 1) for _ in range(n + 1)]
for i in range(n + 1):
dp[i][0] = True
for i in range(1, n + 1):
for j in range(1, target + 1):
if j >= nums[i - 1]:
dp[i][j] = dp[i-1][j] or dp[i-1][j - nums[i-1]]
else:
dp[i][j] = dp[i-1][j]
return dp[n][target]
Trong đoạn mã trên, dp[i][j]
biểu thị việc có thể tạo thành tổng j
sử dụng các phần tử từ 1
đến i
.
Bước 2: Điền giá trị vào bảng
Đối với mỗi phần tử trong tập hợp, kiểm tra xem nếu không sử dụng phần tử đó (kế thừa giá trị từ hàng trên) hoặc sử dụng phần tử đó (kiểm tra giá trị tại vị trí j - nums[i-1]
). Giá trị True
tại dp[n][target]
chứng tỏ có thể chia tập hợp thành hai phần có cùng tổng.
Tối ưu hóa dung lượng bộ nhớ
Sử dụng cách tiếp cận bằng mảng 1 chiều giúp tiết kiệm bộ nhớ đáng kể:
def can_partition(nums):
total_sum = sum(nums)
if total_sum % 2 != 0:
return False
target = total_sum // 2
dp = [False] * (target + 1)
dp[0] = True
for num in nums:
for j in range(target, num - 1, -1):
dp[j] = dp[j] or dp[j - num]
return dp[target]
Trong phiên bản này, chỉ có một mảng dp
dài target + 1
, sử dụng kỹ thuật cập nhật ngược để tránh ghi đè thông tin quan trọng.
Tổng kết
Giải quyết Partition Problem đòi hỏi sự hiểu biết sâu sắc về cấu trúc dữ liệu cũng như kỹ thuật quy hoạch động. Triển khai một thuật toán hiệu quả không chỉ giúp ta giải quyết bài toán này mà còn cung cấp nhiều kinh nghiệm quý báu trong việc xây dựng các giải pháp tối ưu cho những bài toán phức tạp khác trong lập trình.
Comments