简单谈谈实现递归暴力枚举
2017-03-21 01:08
489 查看
简单谈一谈如何用递归实现暴力枚举。
下面先看到一个例子。
袋子里有2红3绿5黄球,随机从中摸出8个,打印显示所有组合。
暴力枚举,其实就是实现一颗搜索树。
那很显然这颗搜索树的层数是8层,因为只要摸8个就行了。每个节点拓展出去的儿子节点很显然是10个,因为每次都有10种选择。
那很显然。8层是递归出口。10个是每层要枚举的分支。所以可以写出如下代码。
但是这并不符合题目的要求。因为这样写,就有可能同个字符被取了多次,而很显然每个字符只能被取一次。好的,可以修改代码,得到如下代码。
这样,加个标记数据,就可以保证在某个方案里,一个字符只能被取一次。但是,发现,这个方法还是不行,因为一个节点发射出去的儿子节点,最多只能是3个(“r”,”g”,”y”),而不能是(“r”,”r”,”g”,”g”“y”…),否则同种方案会被计算多次。那也就是说,在每一层搜索的时候,搜过的字符就不能再搜了。由于字符数组是有序的,所以用如下代码就可以实现上述功能。
好了,现在已经快实现了,就差最后一步了。现在有个问题就是。可能会搜出”gggrryyy”,”gggryyyr”,那由于取得是组合,所以这两种也应该算同种方案。那如果解决这个问题呢?其实很简单,比如这次搜索,把s第i个儿子给了ans[d],那么下一次搜索,枚举儿子分支的时候就从i+1开始。这样搜出来的绝对是有序的,就不会出现无序的情况。
代码如下:
主函数里直接
这样,这个问题就解决了。
下面再看到一个问题。
输入n(1-10之间数字),将数字分解显示,如6可以显示为6,5+1,4+2,4+1+1…..
同样,在写代码之前,脑袋里要有一颗搜索树。
但是,这里递归层数就不那么明确了。那可以用一个状态sum来确定递归出口。可以写出代码。
很容易发现,怎么输出解啊。我怎么知道加数有多少个呀?
简单,再加个形参记录层数就可以了,刚好第d层可以放第d个加数。
可以写出代码。
紧接着,你会发现你输出了”4+2”,又输出了”2+4”
这其实是同一种情况,同样的想法,只要让后面的数比前一个取的数小于等于就可以了。可以写出代码。
完美解决问题。
那现在这个问题会解决了吗?
用递归实现,输出用1分、2分和5分的硬币凑成1元,一共有多少种方法。
不确定层数,又是组合问题,所以可以先定个sum的状态,然后下一次取一定要大于等于上一次所取的,避免方案重复。
最后附上这三道题的完整代码。
摸球
n的所有和
凑硬币
下面先看到一个例子。
袋子里有2红3绿5黄球,随机从中摸出8个,打印显示所有组合。
暴力枚举,其实就是实现一颗搜索树。
那很显然这颗搜索树的层数是8层,因为只要摸8个就行了。每个节点拓展出去的儿子节点很显然是10个,因为每次都有10种选择。
那很显然。8层是递归出口。10个是每层要枚举的分支。所以可以写出如下代码。
char s[]="gggrryyyyy";//存放颜色数据 char ans[10];//存放当前方案 void dfs(int d) { if(d==8) { for(int i=0;i<d;i++) printf("%c",ans[i]); printf("\n"); return ; } for(int i=0;i<10;i++) { ans[d]=s[i]; dfs(i); } }
但是这并不符合题目的要求。因为这样写,就有可能同个字符被取了多次,而很显然每个字符只能被取一次。好的,可以修改代码,得到如下代码。
int vis[15]; char s[]="gggrryyyyy";//存放颜色数据 char ans[10];//存放当前方案 void dfs(int d) { if(d==8) { for(int i=0;i<d;i++) printf("%c",ans[i]); printf("\n"); return ; } for(int i=0;i<10;i++) { if(vis[i]==0) { vis[i]=1; ans[d]=s[i]; dfs(i); vis[i]=0; } } }
这样,加个标记数据,就可以保证在某个方案里,一个字符只能被取一次。但是,发现,这个方法还是不行,因为一个节点发射出去的儿子节点,最多只能是3个(“r”,”g”,”y”),而不能是(“r”,”r”,”g”,”g”“y”…),否则同种方案会被计算多次。那也就是说,在每一层搜索的时候,搜过的字符就不能再搜了。由于字符数组是有序的,所以用如下代码就可以实现上述功能。
int vis[15]; char s[]="gggrryyyyy";//存放颜色数据 char ans[10];//存放当前方案 void dfs(int d) { if(d==8) { for(int i=0;i<d;i++) printf("%c",ans[i]); printf("\n"); return ; } int f=-1; for(int i=0;i<10;i++) { if(vis[i]==0) { if(f==-1|| f!=s[i]) { f=s[i]; vis[i]=1; ans[d]=s[i]; dfs(i); vis[i]=0; } } } }
好了,现在已经快实现了,就差最后一步了。现在有个问题就是。可能会搜出”gggrryyy”,”gggryyyr”,那由于取得是组合,所以这两种也应该算同种方案。那如果解决这个问题呢?其实很简单,比如这次搜索,把s第i个儿子给了ans[d],那么下一次搜索,枚举儿子分支的时候就从i+1开始。这样搜出来的绝对是有序的,就不会出现无序的情况。
代码如下:
char s[]="gggrryyyyy"; int vis[15]; char ans[10]; void dfs(int d,int last) { if(d==8) { for(int i=0;i<8;i++) cout<<ans[i];cout<<endl; return ; } int f=-1; for(int i=last;i<10;i++) { if(vis[i]==0) { if(f==-1|| f!=s[i]) { f=s[i]; vis[i]=1; ans[d]=s[i]; dfs(d+1,i+1); vis[i]=0; } } } }
主函数里直接
dfs(0,0);就可以了。
这样,这个问题就解决了。
下面再看到一个问题。
输入n(1-10之间数字),将数字分解显示,如6可以显示为6,5+1,4+2,4+1+1…..
同样,在写代码之前,脑袋里要有一颗搜索树。
但是,这里递归层数就不那么明确了。那可以用一个状态sum来确定递归出口。可以写出代码。
void dfs(int sum) { if(sum==0) { 输出一组解 return ; } for(int i=sum;i>0;i--) { 取一个加数为i dfs(sum-i); } }
很容易发现,怎么输出解啊。我怎么知道加数有多少个呀?
简单,再加个形参记录层数就可以了,刚好第d层可以放第d个加数。
可以写出代码。
void dfs(int d,int sum) { if(sum==0) { for(int i=0;i<d-1;i++) cout<<ans[i]<<"+";cout<<ans[d-1]<<endl; return ; } for(int i=sum;i>0;i--) { ans[d]=i; dfs(d+1,sum-i); } }
紧接着,你会发现你输出了”4+2”,又输出了”2+4”
这其实是同一种情况,同样的想法,只要让后面的数比前一个取的数小于等于就可以了。可以写出代码。
void dfs(int d,int sum,int pre) { if(sum==0) { for(int i=0;i<d-1;i++) cout<<ans[i]<<"+";cout<<ans[d-1]<<endl; return ; } for(int i=sum;i>0;i--) { ans[d]=i; if(i<=pre) dfs(d+1,sum-i,i); } }
完美解决问题。
那现在这个问题会解决了吗?
用递归实现,输出用1分、2分和5分的硬币凑成1元,一共有多少种方法。
不确定层数,又是组合问题,所以可以先定个sum的状态,然后下一次取一定要大于等于上一次所取的,避免方案重复。
最后附上这三道题的完整代码。
摸球
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
char s[]="gggrryyyyy"; int vis[15]; char ans[10]; void dfs(int d,int last) { if(d==8) { for(int i=0;i<8;i++) cout<<ans[i];cout<<endl; return ; } int f=-1; for(int i=last;i<10;i++) { if(vis[i]==0) { if(f==-1|| f!=s[i]) { f=s[i]; vis[i]=1; ans[d]=s[i]; dfs(d+1,i+1); vis[i]=0; } } } }
using namespace std;
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
dfs(0,0);
return 0;
}
n的所有和
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
int ans[10];
void dfs(int d,int sum,int pre) { if(sum==0) { for(int i=0;i<d-1;i++) cout<<ans[i]<<"+";cout<<ans[d-1]<<endl; return ; } for(int i=sum;i>0;i--) { ans[d]=i; if(i<=pre) dfs(d+1,sum-i,i); } }
using namespace std;
int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int n;
while(cin>>n)
dfs(0,n,n);
return 0;
}
凑硬币
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #define LL long long using namespace std; int ans=0; int a[105]={5,2,1}; void dfs(int cur,int last) { if(cur==0) { ans++; return ; } for(int i=last;i<3;i++) if(cur>=a[i]) dfs(cur-a[i],i); } int main() { //freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); int n; for(int i=100;i<=100;i++) { ans=0; dfs(i,0); printf("%d\n",ans); } return 0; }
相关文章推荐
- 初学者看过来:简单谈谈 C/C++ 递归的思想,实现,以及和循环的关系。
- 简单谈谈 C/C++ 递归的思想,实现,以及和循环的关系。
- 简单谈谈 C/C++ 递归的思想,实现,以及和循环的关系
- 初学者看过来:简单谈谈 C/C++ 递归的思想,实现,以及和循环的关系
- zoj 1716 又是一道简单题。直接暴力枚举实现!
- 初学者看过来:简单谈谈 C/C++ 递归的思想,实现,以及和循环的关系。
- 利用一个简单的递归实现打印目录的层次结构
- [百度笔试题]一个简单的递归实现
- 用递归方法实现指定目录的枚举——兼谈File类的使用
- UVa 216 - Getting in Line 回溯,简单递归枚举
- 递归实现回文判断(如:abcdedbca就是回文,判断一个面试者对递归理解的简单程序)
- SQL2000 存储过程中实现递归的一个简单例子
- DataTable 递归 简单的程序,来实现无限级列表 结合 jquery.table.js 实现
- 实现递归的简单实例
- 百度笔试题---一个简单的递归实现
- 用MASM写一个简单的实现递归操作的汇编程序,所谓递归,上课已经跟大家说清楚了,如果我们只考虑简单的只分一次的递
- [专题]暴力之简单枚举
- linux下递归删除文件夹,简单实现rm的功能
- 简单全排列C递归实现 没考虑重复情况
- [C++]数据结构实验01:使用递归实现简单的全排列