Cho danh sách số dương nums. Phép toán: chọn a ≤ b, nếu a < b thì xóa cả hai và thêm b-a vào danh sách. Lặp lại tùy ý. Tìm số nhỏ nhất có thể còn lại (0 nếu rỗng).
Ví dụ
Input: nums = [2, 4, 5]
Output: 1
Giải thích: Chọn 4,5 → thêm 1 → [2,1]. Chọn 2,1 → thêm 1 → [1].
Thuật toán (Quy hoạch động / Subset Sum)
Bài toán tương đương: chia tập hợp thành 2 nhóm sao cho hiệu 2 tổng nhỏ nhất (tức là tổng 2 nhóm gần nhau nhất).
- Tổng
S = sum(nums). - Tìm tập con có tổng gần
S/2nhất → hiệu nhỏ nhất =|S - 2*subset_sum|. - Đệ quy thử chọn/bỏ mỗi phần tử.
Mã nguồn Python
def solve(nums):
S = sum(nums)
def f(i, s):
if i >= len(nums):
return s
n = nums[i]
if s - 2 * n < 0:
return f(i + 1, s)
return min(f(i + 1, s - 2 * n), f(i + 1, s))
return f(0, S)
# Test
nums = [2, 4, 5]
print(solve(nums)) # Output: 1
Độ phức tạp
- Thời gian:
O(2ⁿ)(đệ quy nhánh). Có thể tối ưu bằng DP bottom-upO(n*S). - Không gian:
O(n)stack đệ quy.
Lưu ý: Đây là bài toán Partition Problem (NP-hard), giải pháp đệ quy chỉ phù hợp n nhỏ. Với n lớn, dùng DP bitset hoặc mảng boolean.