给出一个集合和一个数值total,输出用哪几个数可以组出total。
参考链接:http://algorithms.tutorialhorizon.com/dynamic-programming-subset-sum-problem/
DP方程为
dp[i][j]=
- dp[i-1][j] if a[j]>j
- dp[i-1][j-a[j]] if a[j]<=j
方程的形式同背包问题如出一辙,只不过这个不涉及到重量、价值问题,而只涉及能不能放下【确切的说应该是放满】
本来是想拿这个去做Combination Sum的,发现那个是回溯而并不是DP。
至于取出所有可能的集合,我写了一个DFS去递归寻找可能的解。
不过可能不是很严谨
正确求法:
- Start from the bottom-right corner and backtrack and check from the True is coming.
- If value in the cell above if false that means current cell has become true after including the current element. So include the current element and check for the sum = sum — current element.
class Solution {
public:
// bool dp[n][target+1];//dp[i][j]表示放入前i个物品,容量为j的时候最大价值
bool dp[1010][1010];
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
//01背包
int n=candidates.size();
//使之有序
sort(candidates.begin(),candidates.end());
memset(dp,0,sizeof(dp));
//初始化第一行数据
for(int i=0;i<n;i++) dp[i][0]=true;
for(int i=0;i<n;i++){
//背包容量遍历
for(int j=1;j<=target;j++){
if(candidates[i]==j) dp[i][j]=true;
else if(candidates[i]>j) dp[i][j]=dp[i-1][j];
else{
if(i>0)
dp[i][j]=dp[i-1][j-candidates[i]];
else
dp[i][j]=false;
}
}
}
for(int i=0;i<n;i++){
//背包容量遍历
for(int j=1;j<=target;j++){
printf("%d ",dp[i][j]);
}
printf("\n");
}
printf("\n");
vector<vector<int> > result;
vector<int> temp;
dfs(temp,candidates,n-1,target,result);
return result;
// //得到dp数组之后,需要遍历一下dp[i][target]==true的地方开始,向后回溯找所有的数值
}
void dfs(vector<int> &temp,vector<int>& candidates,int start,int capacity,vector<vector<int> >& result){
//已经装满了
if(capacity==0){
result.push_back(temp);
return;
}
//从后往前找
for(int i=start;i>=0;i--){
//说明这一栏是true
if(dp[i][capacity]){
//防止从后往前
// if(capacity-candidates[i] <= candidates[i]){
temp.push_back(candidates[i]);
dfs(temp,candidates,start-1,capacity-candidates[i],result);
temp.pop_back();
// }
}
}
}
};