thay đổi cách tiếp cận thôi bạn, viết đệ quy thì top-down còn muốn khử đệ quy thì bottom-up.
Python:
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
dp = defaultdict(int)
dp[0] = 1
for num in nums:
tmp_dp = dp
dp = defaultdict(int)
for key,value in tmp_dp.items():
dp[key+num] += value
dp[key-num] += value
return dp[target]
Cách này là phiên bản khử đệ quy của cách giái trước đó của t. Tuy nhiên nó chưa thực sự tối ưu. Do nó phải tính hết toàn bộ giá trị có thể sinh ra bởi tập ban đầu. Bản chất là vét cạn thôi, nhưng do giá trị trung gian được lưu lại bởi cái dict nên giảm thiểu được việc tính toán lại. Độ phức tạp của cách này là O(2^n). Do số lượng phần tử của tập ban đầu <= 20 nên cách này k bị timeout.
Cách tối ưu hơn là chuyển về bài toán tìm số tập con có tổng bằng 1 số cho trước.
Giả sử mình có s1 là tổng của các phần tử được thêm dấu +, s2 là tổng của các phần tử được thêm dấu '-'.
s1 + s2 = sum(nums)
s1- s2 = target
=> s1 = (sum + target)/2, s2 = (sum - target)/2
bài toán quy về tìm các tập con có tổng bằng s1 hoặc s2.
Mình sẽ chọn min(s1,s2) làm giá trị cần tìm để giảm không gian tìm kiếm lại, giúp tối ưu hơn.
Ngoài ra +/- 0 thì không làm thay đổi kết quả, do đó mình sẽ đếm số lượng phần tử 0, remove ra khỏi tập ban đầu. Kết quả cuối cùng sẽ *2^numZero. (do mỗi 1 phần tử 0 thì đều có 2 cách chọn +/-). Cái này cũng giúp lời giải tối ưu hơn do giảm được số lượng phần tử cần tính.
Code:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
// get new target
int sum = accumulate(nums.begin(), nums.end(), 0);
if ((sum + target)%2 != 0 || target > sum || target < -sum) return 0;
int s1 = (sum + target)/2;
int s2 = (sum - target)/2;
int new_target = min(s1,s2);
//count number 0 and remove all element equal to 0
int numZero = count(nums.begin(), nums.end(), 0);
nums.erase(remove(nums.begin(), nums.end(), 0), nums.end());
vector<vector<int>> dp(nums.size()+1, vector<int>(new_target + 1, 0));
dp[0][0] = 1;
for (int i = 0; i < nums.size(); ++i) {
for (int j = 0; j <= new_target; ++j) {
dp[i+1][j] += dp[i][j];
if (j + nums[i] <= new_target) dp[i+1][j + nums[i]] += dp[i][j];
}
}
return dp.back().back()*pow(2, numZero);
}
};