回溯算法之幸运的袋子

Posted 快乐江湖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯算法之幸运的袋子相关的知识,希望对你有一定的参考价值。

解法框架

【README】回溯算法基本框架

幸运的袋子(点击跳转)

在这里插入图片描述

这道题基本也是回溯,题解中有一部分提到了dfs,其实dfs本质也是回溯算法。这道题有点特别,除了考察算法外,还轻微涉及一点数学知识,其实也可以说是规律吧。此题本质就是在集合中找到符合条件的子集,这条件就是子集中所有数字的和大于所有数字的积

规律如下

  1. 数论中:对于两个任意的正整数a和b,如果要满足a+b<ab,则必须要有一个数为1
  2. 我们将所有的球排好序后,假设a1,a2,a3不满足条件,那么也就是sum<=multi,如果要选择一个数b使得sum+b>multi*b,那么b一定为1。但是如果选择的b>1,那么一定不满足条件,同时排在b后面的数组更不可能满足条件
  3. 如果有连续的1存在,且当前位置不满足条件,但是该位置的数字为1,那么应该继续向后找寻,因为只要是1就能增大sum,而不会使multi变大
  4. 对于重复的数字只算一次,也就是咋们在回溯算法中说到过的在选择列表中选择正确的选项

首先,这道题要完全自己输入和输出,先打起框架。咋们的选择列表,也就是所有球的编号,依次存放在一个vector中,接着将球按照编号从小到大的排序

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    int n;//接受几个球
    cin>>n;
    vector<int> ball;
    for(int i=0;i<n;i++)
    {
        int temp;
        cin>>temp;
        ball.push_back((temp));
    }
    sort(ball.begin(),ball.end());//排序

}

选择列表有了,下一个就是路径,这个路径非常简单,只需要一个整型ret即可,因为当sum>multi的时候,这表明现在就已经可以作为一个幸运的袋子了,所以ret增加。 所以将ret设置为全局变量。

对于递归函数,第一个参数一定是选择列表,第二个参数是球的个数,第三个参数要设置为一个pos,因为递归函数进行for循环时一定要一个正确的起始位置,for循环每次就从i=pos的位置,递归时这个球加入了,我们就pos=i+1,表明选择下一个球。剩下两个参数分别为sum和multi用于计算判断

int ret=0;//幸运的袋子个数
void back(vector<int>& ball,int n,int pos,int sum,int multi)
{
    for(int i=pos;i<n;i++)
    {
        sum+=ball[i];//和
        multi*=ball[i];//乘积
        if(sum>multi)//ball[i]这个球加入后符合幸运袋子
        {
            ret+=1;
            back(ball,n,i+1,sum,multi);//继续回溯,下一个位置
        }
        else if(ball[i]==1)//如果不满足条件,但是这一位是1,是还有机会的,所以递归就可以了
        {
            back(ball,n,i+1,sum,multi);
        }
        else
        {
            break;//不满足直接退出
        }
        sum-=ball[i];//撤销选择
        multi/=ball[i];//撤销选择
        while(i<n-1 && ball[i]==ball[i+1])//连续的只选择1次
        {
            ++i;
        }
        
        
    }

}

需要注意的是去重要写在后面,不要一上来就去重

最后完善即可,注意multi在传入时要传入1,不要传入0

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int ret=0;//幸运的袋子个数
void back(vector<int>& ball,int n,int pos,int sum,int multi)
{
    for(int i=pos;i<n;i++)
    {
        sum+=ball[i];//和
        multi*=ball[i];//乘积
        if(sum>multi)//ball[i]这个球加入后符合幸运袋子
        {
            ret+=1;
            back(ball,n,i+1,sum,multi);//继续回溯,下一个位置
        }
        else if(ball[i]==1)//如果不满足条件,但是这一位是1,是还有机会的,所以递归就可以了
        {
            back(ball,n,i+1,sum,multi);
        }
        else
        {
            break;//不满足直接退出
        }
        sum-=ball[i];//撤销选择
        multi/=ball[i];//撤销选择
        while(i<n-1 && ball[i]==ball[i+1])//连续的只选择1次
        {
            ++i;
        }
    }
}

int main()
{
    int n;//接受几个球
    cin>>n;
    vector<int> ball;
    for(int i=0;i<n;i++)
    {
        int temp;
        cin>>temp;
        ball.push_back((temp));
    }
    sort(ball.begin(),ball.end());//排序
    back(ball,n,0,0,1);
    cout<<ret<<endl;
    return 0;

}

在这里插入图片描述

以上是关于回溯算法之幸运的袋子的主要内容,如果未能解决你的问题,请参考以下文章

幸运的袋子(深度优先遍历)

2017校招真题在线编程-幸运的袋子

每日一题 | day14(计算日期到天数转换 | 幸运的袋子)

回溯算法之迷宫问题

五大算法之回溯算法

17图的搜索算法之回溯法