We show three approaches: plain recursion (exponential), recursion with memoization (top-down DP), and an iterative bottom-up DP (tabulation). The bottom-up DP is the recommended solution in practice for moderate V.
classSolution {
public:// Naive recursive backtracking: O(2^n) time
int countSubsets(const std::vector<int>& a, int V) {
returnrec(a, (int)a.size() -1, V);
}
private:int rec(const std::vector<int>& a, int i, int rem) {
if (rem ==0) return1;
if (rem <0|| i <0) return0;
// exclude
int ans = rec(a, i -1, rem);
// include
ans += rec(a, i -1, rem - a[i]);
return ans;
}
};
classSolution {
// Naive recursive backtrackingpublicintcountSubsets(int[] a, int V) {
return rec(a, a.length- 1, V);
}
privateintrec(int[] a, int i, int rem) {
if (rem == 0) return 1;
if (rem < 0 || i < 0) return 0;
int ans = rec(a, i - 1, rem); // exclude ans += rec(a, i - 1, rem - a[i]); // includereturn ans;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
classSolution:
# Naive recursive backtrackingdefcountSubsets(self, a: list[int], V: int) -> int:
defrec(i: int, rem: int) -> int:
if rem ==0:
return1if rem <0or i <0:
return0 ans = rec(i -1, rem) # exclude ans += rec(i -1, rem - a[i]) # includereturn ans
return rec(len(a) -1, V)
Many recursive subproblems repeat: count(i, rem) is called multiple times with the same parameters. Cache results in a map keyed by (i, rem) to avoid recomputation.
classSolution {
public:int countSubsets(const std::vector<int>& a, int V) {
memo.clear();
returndfs(a, V, (int)a.size() -1);
}
private: std::unordered_map<longlong,int> memo; // key = (rem<<32) | i
intdfs(const std::vector<int>& a, int rem, int i) {
if (rem ==0) return1;
if (rem <0|| i <0) return0;
longlong key = ((longlong)rem <<32) | (unsignedint)i;
auto it = memo.find(key);
if (it != memo.end()) return it->second;
int ans = dfs(a, rem, i -1);
if (rem >= a[i]) ans += dfs(a, rem - a[i], i -1);
memo[key] = ans;
return ans;
}
};
classSolution {
private Map<String,Integer> memo;
publicintcountSubsets(int[] a, int V) {
memo =new HashMap<>();
return dfs(a, V, a.length- 1);
}
privateintdfs(int[] a, int rem, int i) {
if (rem == 0) return 1;
if (rem < 0 || i < 0) return 0;
String key = rem +":"+ i;
if (memo.containsKey(key)) return memo.get(key);
int ans = dfs(a, rem, i - 1);
if (rem >= a[i]) ans += dfs(a, rem - a[i], i - 1);
memo.put(key, ans);
return ans;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
classSolution:
defcountSubsets(self, a: list[int], V: int) -> int:
from functools import lru_cache
@lru_cache(maxsize=None)
defdfs(i: int, rem: int) -> int:
if rem ==0:
return1if rem <0or i <0:
return0 ans = dfs(i -1, rem)
if rem >= a[i]:
ans += dfs(i -1, rem - a[i])
return ans
return dfs(len(a) -1, V)
classSolution {
public:// Bottom-up DP (1D optimized)
int countSubsets(const std::vector<int>& a, int V) {
std::vector<longlong> dp(V+1, 0);
dp[0] =1;
for (int num : a) {
for (int s = V; s >= num; --s) dp[s] += dp[s - num];
}
return (int)dp[V];
}
};
classSolution {
// Bottom-up DP (1D optimized)publicintcountSubsets(int[] a, int V) {
int[] dp =newint[V+1];
dp[0]= 1;
for (int num : a) {
for (int s = V; s >= num; --s) dp[s]+= dp[s - num];
}
return dp[V];
}
}
1
2
3
4
5
6
7
8
classSolution:
defcountSubsets(self, a: list[int], V: int) -> int:
dp = [0] * (V +1)
dp[0] =1for num in a:
for s in range(V, num -1, -1):
dp[s] += dp[s - num]
return dp[V]