Combination Sum:
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, � , ak) must be in non-descending order. (ie, a1 ? a2 ? � ? ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7
and target 7
,
A solution set is:
[7]
[2, 2, 3]
class Solution {
public:
vector<vector<int> > combinationSum(vector<int> &can, int tar) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
sort(can.begin(),can.end());
vector<vector<int> > ans;
vector<int> num;
int sum=0;
solve(ans,can,num,0,sum,tar);
return ans;
}
void solve(vector<vector<int> >& ans,vector<int>& can,vector<int>& num,int k,int sum,int tar)
{
int n =can.size();
if ( k>=n|| sum>tar || sum+can[k]>tar )
return;
if ( (tar-sum)%can[k]==0 )
{
int tn=(tar-sum)/can[k];
for(int t=0;t<tn;t++)
num.push_back(can[k]);
ans.push_back(num);
for(int t=0;t<tn;t++)
num.pop_back();
}
int maxN=(tar-sum)/can[k]-1;
solve(ans,can,num,k+1,sum,tar);
int i=0;;
while(i<maxN)
{
num.push_back(can[k]);
i++;
sum+=can[k];
if ( sum == tar )
ans.push_back(num);
if (k>=n-1|| sum+can[k+1]>tar)
break;
solve(ans,can,num,k+1,sum,tar);
}
while(i--)
num.pop_back();
}
};
第二种思路呢,就是很明显是个多重背包的问题,解多重背包是很容易的,麻烦的是要求路径,所以求路径还是要用DFS搜索。
结果由于DFS的原因,貌似时间并没有比上面的直接搜索要快一些,但也有可能是剪枝剪得不好啦。
class dpSolution {
public:
vector<vector<int> > combinationSum(vector<int> &can, int tar) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
vector<vector<int> > ans;
int n = can.size();
sort(can.begin(),can.end());
int m=n;
for(int i=0;i<n;i++)
{
if(can[i]>tar)
{
m=i;
break;
}
}
if ( m==0 ) return ans;
vector<vector<int> > dp(m,vector<int>(tar+1,0));
int tmp=0;
while(tmp<=tar)
{
dp[0][tmp]=1;
tmp+=can[0];
}
for(int i=1;i<m;i++)
{
for(int j=0;j<can[i];j++)
dp[i][j]=dp[i-1][j];
for(int j=can[i];j<tar+1;j++)
{
if ( j%can[i]==0 )
dp[i][j]=1;
else
dp[i][j]=dp[i][j-can[i]]||dp[i-1][j];
}
}
vector<int> nums;
dfs(m-1,tar,dp,ans,nums,can);
for(int i=0;i<ans.size();i++)
reverse(ans[i].begin(),ans[i].end());
return ans;
}
void dfs(int k,int sum,vector<vector<int> >& dp,vector<vector<int> >& ans,vector<int>& nums,vector<int>& can)
{
if ( k<0 || sum<=0 )
return;
if ( dp[k][sum]== 0 )
return;
int tSum=sum;
int in=0;
while(tSum>0)
{
if ( dp[k][tSum]!=1 )
break;
nums.push_back(can[k]);
in++;
tSum-=can[k];
if ( tSum==0 )
ans.push_back(nums);
dfs(k-1,tSum,dp,ans,nums,can);
}
while(in--)
nums.pop_back();
dfs(k-1,sum,dp,ans,nums,can);
}
};
之前这个多重背包写得好丑。。。新写一遍的好看多了。
class Solution {
public:
vector<vector<int> > combinationSum(vector<int> &can, int target) {
// Note: The Solution object is instantiated only once and is reused by each test case.
sort(can.begin(),can.end());
int k=0;
while(k<can.size()&&can[k]<=target)
k++;
if(k<=0)
return vector<vector<int> >();
vector<vector<bool> > dp(k,vector<bool>(target+1,false));
for(int i=0;i<=target;i++)
if(i%can[0]==0)
dp[0][i]=true;
for(int i=1;i<k;i++)
{
for(int j=0;j<can[i];j++)
dp[i][j]=dp[i-1][j];
for(int j=can[i];j<=target;j++)
dp[i][j]=dp[i-1][j]||dp[i][j-can[i]];
}
vector<vector<int> > ans;
vector<int> path;
createPath(ans,path,k-1,target,dp,can);
return ans;
}
void createPath(vector<vector<int> >& ans,vector<int>& path,int k,int tar,vector<vector<bool> >& dp,vector<int>& can)
{
if(tar==0)
{
reverse(path.begin(),path.end());
ans.push_back(path);
reverse(path.begin(),path.end());
return ;
}
if(k<0 || !dp[k][tar])
return ;
if(k>0&&dp[k-1][tar])
createPath(ans,path,k-1,tar,dp,can);
if (tar>=can[k]&&dp[k][tar-can[k]])
{
path.push_back(can[k]);
createPath(ans,path,k,tar-can[k],dp,can);
path.pop_back();
}
}
};
Combination Sum II:
Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
Each number in C may only be used once in the combination.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, � , ak) must be in non-descending order. (ie, a1 ? a2 ? � ? ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 10,1,2,7,6,1,5
and target 8
,
A solution set is:
[1, 7]
[1, 2, 5]
[2, 6]
[1, 1, 6]
中间有个问题,比如 原数组是[1,1,1,1,1,],最后目标值是1,那么求出的ans会有5个{1},明显是不对的,我的办法是求出ans之后排序然后取出重复,这样的效率明显是不好的,有没有办法在求的过程中就不要生成这样重复的答案呢,想一想。
#define vi vector<int>
#define vvi vector<vi >
#define pb push_back
class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int n=num.size();
sort(num.begin(),num.end());
int m=n;
for(int i=0;i<n;i++)
{
if ( num[i] > target )
{
m=i;
break;
}
}
vvi ans;
vi path;
solve(ans,path,num,0,m,0,target);
sort(ans.begin(),ans.end());
ans.erase(unique(ans.begin(),ans.end()),ans.end());
return ans;
}
void solve(vvi& ans,vi& path,vi& num,int k,int m,int sum,int target)
{
if ( k >=m || sum>target || sum+num[k]>target )
return;
solve(ans,path,num,k+1,m,sum,target);
path.pb(num[k]);
sum+=num[k];
if ( sum==target )
ans.pb(path);
solve(ans,path,num,k+1,m,sum,target);
path.pop_back();
}
};
昨天说到怎么可以在搜索的时候就避免重复结果的出现,其实比较简单啦。
比如对 { 5,5,5} ,target=5 来说,为了不产生3次{ 5} 。首先我们看这3个{5}是怎么产生的,如果我们用0和1表示该位置的数取和不取,那3个{5}对应的是{1,0,0}、{0,1,0}和{0,0,1}三种。在这些情况里面1和0是不连续的,如果我们可以让取和不取是连续的,即总是{1,1,1,0,0。。。}这种情况,那最后肯定是没有重复答案啦。
所以我们的剪枝策略是:
当当前元素跟前一个元素是相同的时候,如果前一个元素被取了,那当前元素可以被取,也可以不取,反过来如果前一个元素没有取,那我们这个以及之后的所以相同元素都不能被取。
#define vi vector<int>
#define vvi vector<vi >
#define pb push_back
class Solution {
public:
vector<vector<int> > combinationSum2(vector<int> &num, int target) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int n=num.size();
sort(num.begin(),num.end());
int m=n;
for(int i=0;i<n;i++)
{
if ( num[i] > target )
{
m=i;
break;
}
}
vvi ans;
vi path;
solve(ans,path,num,0,m,0,target,0);
//sort(ans.begin(),ans.end());
//ans.erase(unique(ans.begin(),ans.end()),ans.end());
return ans;
}
void solve(vvi& ans,vi& path,vi& num,int k,int m,int sum,int target,int pre)
{
if ( k >=m || sum>target || sum+num[k]>target )
return;
solve(ans,path,num,k+1,m,sum,target,0);
if ( k==0 || num[k]!=num[k-1] || pre==1 )
{
path.pb(num[k]);
sum+=num[k];
if ( sum==target )
ans.pb(path);
solve(ans,path,num,k+1,m,sum,target,1);
path.pop_back();
}
}
};