一.leetcode-39
本题思路与KSum类似。
区别有二:
第一,允许同一个数字多次使用;
第二,答案包括的1Sum,2Sum,……,KSum的结果,而不仅仅只有KSum。
代码如下:
class Solution {
public:
/*
思路:第一,先升序排序,然后去掉其中的重复项,得到C。
第二,T/C[0] = n,即最多n个数的和,最多为NSum。
第三,允许单个数字多次使用,与之前的KSum稍有不同。
*/
vector<vector<int> > result;//保存结果
void KSum(int k, vector<int> tmp, vector<int> cur_re, int target){
//vector<int> cur_re;//保存中间结果
if(k==1)
{
for(size_t i=0; i != tmp.size();++i)
{
if(tmp[i] == target)//找到目标
{
cur_re.push_back(tmp[i]);//压入中间结果
result.push_back(cur_re);//存入最终结果
cur_re.pop_back();//清除最近压入的数据,方便下一次循环
return;
}
}
}
else{
while(tmp.size())
{
cur_re.push_back(tmp[0]);//压入中间结果
KSum(k-1,tmp, cur_re, target-tmp[0]);//进入更深一层
tmp.erase(tmp.begin());//去掉tmp的第一个,依次递增向后检索
cur_re.pop_back();//清除最近压入的数据,方便下一次循环
}
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> tmp_pre = candidates;
sort(tmp_pre.begin(), tmp_pre.end());//第一步,排序
vector<int> tmp;
tmp.push_back(tmp_pre[0]);//压入第一个
//去掉其中的重复项
for(size_t i = 1; i != tmp_pre.size(); ++i)
{
if(tmp_pre[i-1] != tmp_pre[i])
tmp.push_back(tmp_pre[i]);
}
int k = target/tmp[0];//第二步,最多为KSum
vector<int> cur_re;//保存中间结果
for(int i=1; i <= k; ++i)//i记录从1Sum,2Sum,,,KSum
{
cur_re.clear();
KSum(i, tmp, cur_re, target);
}
return result;
}
};
这是很自然直接的思路,通过了leetcode,但是效率不高,时间为269ms。
之后,思考提高效率。
措施:去掉其中的不必要检测。
增加了A,B两处剪枝处理,时间轻松变为135ms,进步一大截,但效率依然很低。
继续剪枝!
增加了C剪枝处理,时间再次轻松变成61ms,哈哈,不错不错。
继续,将A处的调整,与C融合,去除A。时间为55ms。
再次调整C,时间为33ms;
class Solution {
public:
/*
思路:第一,先排序C。
第二,T/C[0] = n,即最多n个数的和,最多为nSum。
第三,允许单个数字多次使用,与之前的KSum稍有不同。
*/
vector<vector<int> > result;//保存结果
void KSum(int k, vector<int> tmp, vector<int> cur_re, int target){
//vector<int> cur_re;//保存中间结果
//A:因为所有数字均大于0,故当target减小,并小于0,则没有答案,直接跳出
// if(target <= 0)
// return;
if(k==1)
{
for(size_t i=0; i != tmp.size();++i)
{
//B:当值大于target,明显没有符合要求的答案,直接跳出循环
if(tmp[i] > target)
break;
if(tmp[i] == target)//找到目标
{
cur_re.push_back(tmp[i]);//压入中间结果
result.push_back(cur_re);//存入最终结果
cur_re.pop_back();//清除最近压入的数据,方便下一次循环
return;
}
}
}
else{
while(tmp.size())
{
//C:当值大于target,明显没有符合要求的答案,直接跳出循环
if(tmp[0]+tmp[0] > target )
break;
cur_re.push_back(tmp[0]);//压入中间结果
KSum(k-1,tmp, cur_re, target-tmp[0]);//进入更深一层
tmp.erase(tmp.begin());//去掉tmp的第一个,依次递增向后检索
cur_re.pop_back();//清除最近压入的数据,方便下一次循环
}
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> tmp_pre = candidates;
sort(tmp_pre.begin(), tmp_pre.end());//第一步,排序
vector<int> tmp;
tmp.push_back(tmp_pre[0]);//压入第一个
//去掉其中的重复项
for(size_t i = 1; i != tmp_pre.size(); ++i)
{
if(tmp_pre[i-1] != tmp_pre[i])
tmp.push_back(tmp_pre[i]);
}
int k = target/tmp[0];//第二步,最多为KSum
vector<int> cur_re;//保存中间结果
for(int i=1; i <= k; ++i)//i记录从1Sum,2Sum,,,KSum
{
//cur_re.clear();
KSum(i, tmp, cur_re, target);
}
return result;
}
};
本人菜鸟,只能到这一步了。
二.leetcode-40
本题与上一题一脉相承,区别是,同一个数字不能重复使用。
class Solution {
/*
思路:
第一,每个数字只使用一次;
第二,不能有重复;
第三,包含从1Sum,2Sum,……,KSum;
*/
vector<vector<int> > result;
void KSum( int k, vector<int> cur_re, vector<int> tmp, int target ){
if( k == 1)
{
for(size_t i=0; i != tmp.size(); ++i)
{
if(tmp[i] > target)
break;
if( target == tmp[i] )
{
cur_re.push_back( tmp[i]);
result.push_back( cur_re);
cur_re.pop_back();
break;
}
}
}
else
{
while(tmp.size())
{
if(tmp[0] > target )
return;
cur_re.push_back( tmp[0]);//压入当前值,并进入下一层次
int temp = tmp[0];
tmp.erase(tmp.begin());//因为不许重复使用,需去掉已经压入的值
// **if(target-tmp[0] < 0)//A:剪枝
// return;**
if(target-temp-tmp[0] < 0)//new_A:剪枝,,
return;
KSum( k-1, cur_re, tmp, target-temp);//递归调用
cur_re.pop_back();
**if(tmp.size()>0)//B:剪枝,这里并不彻底,大于两个相同的,就会导致重复
if(tmp[0] == temp)
tmp.erase(tmp.begin());**
}
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> tmp = candidates;//保存序列
sort(tmp.begin(),tmp.end());//对tmp排序
vector<int> cur_re;//保存中间结果
int k=0;//最多KSum。
int sum=0;
for(size_t i=0; i != tmp.size(); ++i )
{//计算最大的K
if(sum+tmp[i] <= target)
{
sum += tmp[i];
++k;
}
else
break;
}
for(int i=1; i <= k; ++i)//从1Sum,逐渐增加到KSum
{
KSum( i, cur_re, tmp, target);
}
bool tag=false;//标志是否进行了删除操作
if(result.size())
for(size_t j=1; j != result.size();)
{
tag=false;//重置tag
for(size_t i=0; i != j; ++i)
{
if(result[i] == result[j])
{
result.erase(result.begin()+j);
tag = true;
break;
}
}
if( tag )
continue;
else
++j;
}
return result;
}
};
反复修改,终于通过测试,高兴之余也些许沮丧,时间为148ms。
好吧,终于算是功能正确,现在就好好提高效率!
增加A:剪枝,时间变为136ms;
增加B:剪枝,时间变为49ms;
将B改为C:剪枝,时间变为36ms;
屏蔽掉结果去重,时间变为34ms;
终于完全去掉重复项,调整A剪枝的位置,最后时间缩短为17ms;
最终代码如下:
class Solution {
/*
思路:
第一,每个数字只使用一次;
第二,不能有重复;
第三,包含从1Sum,2Sum,……,KSum;
*/
vector<vector<int> > result;
void KSum( int k, vector<int> cur_re, vector<int> tmp, int target ){
if( k == 1)
{
for(size_t i=0; i != tmp.size(); ++i)
{
if(tmp[i] > target)
break;
if( target == tmp[i] )
{
cur_re.push_back( tmp[i]);
result.push_back( cur_re);
cur_re.pop_back();
break;
}
}
}
else
{
while(tmp.size())
{
if(tmp[0]+tmp[1] > target )//A:剪枝
return;
cur_re.push_back( tmp[0]);//压入当前值,并进入下一层次
int temp = tmp[0];
tmp.erase(tmp.begin());//因为不许重复使用,需去掉已经压入的值
KSum( k-1, cur_re, tmp, target-temp);//进入更深一层循环
cur_re.pop_back();
while(tmp.size()>0)//B:剪枝
{
if(tmp[0] == temp)
tmp.erase(tmp.begin());
else break;
}
}
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> tmp = candidates;//保存序列
sort(tmp.begin(),tmp.end());//对tmp排序
vector<int> cur_re;//保存中间结果
int k=0;//最多KSum。
int sum=0;
for(size_t i=0; i != tmp.size(); ++i )//计算最大的K值
{
if(sum+tmp[i] <= target)
{
sum += tmp[i];
++k;
}
else
break;
}
for(int i=1; i <= k; ++i)//从1Sum,逐渐增加到KSum
{
KSum( i, cur_re, tmp, target);
}
return result;
}
};
做到这里,本菜鸟已经束手无策了。
一点教训:对迭代器进行删除操作时候,特别注意迭代器的变化,查了一下,发现,迭代器添加、删除操作会使原来的迭代器失效,下一次遍历时不能继续使用,所以,需要特别小心迭代器的更新。
可以将原来的迭代器删除操作改为:
vector v;
vector::iterator itr;
原来的:v.erase(itr);
改为:itr=v.erase(itr);
原因是:迭代器的erase()函数,返回删除的迭代器的下一个迭代器,如此,进行迭代器的更新,解决问题。
三 .leetcode–216
Combination Sum III Total Accepted: 2260 Total Submissions: 8300 My Submissions Question Solution
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
Ensure that numbers within the set are sorted in ascending order.
Example 1:
Input: k = 3, n = 7
Output:
[[1,2,4]]
Example 2:
Input: k = 3, n = 9
Output:
[[1,2,6], [1,3,5], [2,3,4]]
考虑:每个数只是1~9,观察案例,按照升序,没有重复的答案,答案中的数也没有重复。所以极端情况就是从1加到9.
故可考虑直接用数学的办法。
显示时间竟然为0ms,
渣渣……
代码如下:
//以下的两个变量最好如此,免得传参过程发生意外。由于题目简单,故采用最简单的方法,目标是:只要能完成任务就好
vector<int> tmp;//中间结果
vector<vector<int> > re;//最终结果
class Solution {
public:
void KSum( int count, int sum, int point){
if( count > 1)
{
for(int i = point; i < 10; i++)
{
tmp.push_back(i);
KSum( count-1, sum-i, i+1);
tmp.pop_back();
}
}
else
{
for(int i=point; i < 10; i++)
{
if( sum == i)
{
tmp.push_back(i);
re.push_back( tmp );
tmp.pop_back();
break;
}
else if(sum < i)
break;
}
}
}
vector<vector<int>> combinationSum3(int k, int n) {
int count = k;
int sum = n;
tmp.clear();//这里必须清除干净,免得受到之前结果的影响
re.clear();//这里必须清除干净,免得受到之前结果的影响
if( count > 0 && sum > 0 && count < sum && sum < 9*count)
{
KSum( count, sum , 1 );
}
return re;
}
};