回溯算法之幸运的袋子
Posted 快乐江湖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回溯算法之幸运的袋子相关的知识,希望对你有一定的参考价值。
解法框架
幸运的袋子(点击跳转)
这道题基本也是回溯,题解中有一部分提到了dfs,其实dfs本质也是回溯算法。这道题有点特别,除了考察算法外,还轻微涉及一点数学知识,其实也可以说是规律吧。此题本质就是在集合中找到符合条件的子集,这条件就是子集中所有数字的和大于所有数字的积
规律如下
- 数论中:对于两个任意的正整数a和b,如果要满足a+b<ab,则必须要有一个数为1
- 我们将所有的球排好序后,假设a1,a2,a3不满足条件,那么也就是
sum<=multi
,如果要选择一个数b使得sum+b>multi*b
,那么b一定为1。但是如果选择的b>1,那么一定不满足条件,同时排在b后面的数组更不可能满足条件 - 如果有连续的1存在,且当前位置不满足条件,但是该位置的数字为1,那么应该继续向后找寻,因为只要是1就能增大sum,而不会使multi变大
- 对于重复的数字只算一次,也就是咋们在回溯算法中说到过的在选择列表中选择正确的选项
首先,这道题要完全自己输入和输出,先打起框架。咋们的选择列表,也就是所有球的编号,依次存放在一个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;
}
以上是关于回溯算法之幸运的袋子的主要内容,如果未能解决你的问题,请参考以下文章