leetcode-39&40&216 Combination Sum I&II&III

一.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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值