第九章 递归
9.1 斐波那契数列 见动态规划专题|| 汉诺塔问题
9.2 波兰式求值
dfs() 表示读取一个数字或者运算符并进行相应的atof转换或者递归运算
逆波兰式不需要递归只需要维护一个栈,读到数字就存入(全局)栈中,读到运算符就将栈顶两个元素进行运算,结果存入栈顶
#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 10
#define num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
double dfs()
{
string s;//读入一个数字或者运算符
cin>>s;
if(s[0]=='+')//对运算符进行递归
return dfs()+dfs();
else if(s[0]=='-')
return dfs()-dfs();
else if(s[0]=='*')
return dfs()*dfs();
else if(s[0]=='/')
return dfs()/dfs();
else//对数字进行转换
return atof(s.data());
}
int main()
{
//freopen("input.txt","r",stdin);
printf("%f\n",dfs());
return 0;
}
小技巧: string类型转换成char *
//string ->char * 需要调用函数
const char *c = s.data();//或者 char *c = (char *)s.data();
const char *c = s.c_str();//或者 char *c = (char *)s.c_str();
//char * -> string 直接复赋值
string s = c;
9.3 递归解决排列组合问题
9.3.1 从起点开始递增的排列组合(可重复或者不可重复,且与顺序无关)
可重复 与顺序无关
注意
1 枚举所有起点(main中的for)
2 枚举所有后继(dfs中的for)
3 dfs(出口参数 , 条件参数1 , 条件参数2…)
2749 分解因数(乘法分解)
dfs(int 当前积, int 上一个因子) 表示当积为dangqianji 且上一个因子是shangyigeyinzi的情况下能否完成分解
#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#define MAX 53
#define num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int n,a,ans;
void dfs(int k,int pre)//限定条件:递增 由参数pre实现(k用来判递归出口)
{
if(k==a)
{
ans++;
return ;
}
if(k>a) return ;
for( int i=pre;i<=a;i++)//如果是不重复递增则i=pre+1
{
if(k*i<=a)
dfs(k*i,i);
else break;
}
}
int main()
{
//freopen("input.txt","r",stdin);
cin>>n;
for(int i=0;i<n;i++)
{
ans=0;
cin>>a;
for(int i=2;i<=a;i++) dfs(i,i);//枚举所有起点
cout<<ans<<endl;
}
return 0;
}
1664 放苹果(加法分解) 可以看做是和确定的递增排列,且限定了加的次数(上一题因子分解没限定乘的次数,这里递归函数的参数要多加一个)
dfs(int 当前第几个盘 , int 总苹果 ,int 上一个盘放的苹果数) 表示该情况继续分解
#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 10
#define num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int m,n,k,t;
void dfs(int dangqianpan , int zongpingguo,int 上一个盘放的苹果数 )//这里多加了一个限定条件(加的次数也就是盘子总数)
{
if(zongpingguo==m&&dangqianpan<n)
{
k++;
return ;
}
if(zongpingguo >m||dangqianpan>n) return;
for(int i=上一个盘放的苹果数;i<=m;i++)
dfs(dangqianpan+1,zongpingguo+i,i);
}
int main()
{
//freopen("input.txt","r",stdin);
cin>>t;
for(int i=0;i<t;i++)
{
cin>>m>>n;
k=0;
for(int i=1;i<=m;i++)//遍历可能的起点
dfs(0,i,i);
cout<<k<<endl;
}
return 0;
}
9.3.2 全排列问题(把1~n按照某种顺序不重复地摆放)
不重复 与顺序有关
注意
1 dfs(递归出口参数) 一般为全排列的总位数n
求n个数的全排列个数 || 求n个数的第k个全排列 可以看作不在同一行同一列的皇后
dfs(int 当前第几行) 表示在当前行放该行的数
//定义 vector<int> ans,temp(临时排列和最终排列); bool visit[MAX];(某个数是否已经加入本次排列)
void dfs(int index)
{
if(index==n+1)//注意是n+1,因为dfs(1)从1开始
{
//计数 或者 按照规则检查temp更新ans(如八皇后)
return;
}
for(int i=1;i<=n;i++)//9.3.1 中因为是递增排列所以不需要一个标记数组visit
{//这里在全排列中需要从头开始找没用过的数字
if(!visit[i])
{
temp.push_back(i);
visit[i]=1;
dfs(index+1);
visit[i]=0;//全排列问题需要还原状态
}
}
}
2754 八皇后问题
dfs(int 当前第几行) 表示 在当前行放该行的数
#include <iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<map>
#include <math.h>
#include<algorithm>
#include <string.h>
#define eps 1e-10
#define ll long long
#define da 0x3f3f3f3f
#define xiao -0x3f3f3f3f
using namespace std;
int b;
vector<int > ans,temp;
bool visit[9]={false};
int num;
void dfs(int index)
{
if(cnt==8)//从0开始所以是n
{
num++;//代表找到了第num个全排列
if(num==b)
ans=temp;
return ;
}
if(index>8) return ;
for(int i=0;i<8;i++)
{
if(!visit[i])
{
//遍历之前的皇后(八皇后问题的高校写法是将检查从出口移动到for中)
//所谓回溯也就是将出口处的判断移动到后面的程序中
bool flag=1;
//八皇后只需要检查之前的皇后是否在k=+-1斜线上,因为不在同一行和同一列是必然的
//index为当前行号,外层for是当前列号,内层for中是之前的行号,temp[]为之前的列号
for(int j=0;j<index;j++)
if(abs(index-j)==abs(i-temp[j])) flag=0;//abs保证k=+-1
if(flag)
{
visit[i]=1;
temp.push_back(i);
dfs(cnt+1);
temp.pop_back();
visit[i]=0;
}
}
}
}
int main()
{
//freopen("input.txt","r",stdin);
int n;
cin>>n;
for(int i=0;i<n;i++)
{
ans.clear();
temp.clear();
memset(visit,0,sizeof(visit));
num=0;
cin>>b;
dfs(0);
for(int i=0;i<8;i++)
cout<<ans[i]+1;
cout<<endl;
}
return 0;
}
1321 棋盘问题 全排列计数(相当于在八皇后问题中去掉斜线条件,增加了一个可摆放位置条件和一个总棋子个数条件(皇后中棋子个数一定等于行数,这里可能小于行数因而递归出口))
dfs(int 当前行 , int 当前第几个数)表示在当前行放第几个数 与皇后相比多了一个条件参数,因为皇后问题每一行必定放一个数,所以行数等于棋子个数,也就不需要额外判断棋子数,这里棋盘若在当前行没找到符合条件的位置,仍然需要递归到下一行,所以需要额外参数标识
DFS(index+1,dijigeshu+1);//该行(第index行)找到了能放的位置并放入,到下一行
DFS(index+1,dijigeshu); //该行没有符合条件的位置,直接到下一行
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[10][10]; //记录棋盘位置
bool book[10]; //记录一列是否已经放过棋子
int n,k;
int total,m; //total 是放棋子的方案数 ,m是已放入棋盘的棋子数目
void DFS(int index,int dijigeshu)
{
if(dijigeshu==k)
{
total++;
return ;
}
if(index>k) //边界
return ;
for(int j=0; j<n; j++)
//判断条件,与八皇后相比这里多了判断是否为棋盘区的条件
if(book[j]==0 && a[index][j]=='#')
{
book[j]=1;
DFS(index+1,dijigeshu+1);//该行(第index行)找到了能放的位置并放入,到下一行
book[j]=0;
}
DFS(index+1,dijigeshu); //与八皇后的区别,该行没有符合条件的位置,直接到下一
}
int main()
{
int i,j;
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&n,&k)&&n!=-1&&k!=-1) //限制条件
{
total=0;
m=0;
for(i=0; i<n; i++)
scanf("%s",&a[i]);
memset(book,0,sizeof(book));
DFS(0,0);
printf("%d\n",total);
}
return 0;
}
9.4 草丛问题
2816 红与黑
dfs(int x ,int y) 表示访问点(x,y)
#include<cstdio>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<set>
#include<vector>
#define MAX 21
#define num 32767
#define INF 0x7f7f7f
#define eps 1e-5
using namespace std;
int w,h,qidianx,qidiany;
int X[]={-1,1,0,0};
int Y[]={0,0,-1,1};
char mp[MAX][MAX];
bool visit[MAX][MAX];
bool check(int x ,int y)
{
if(x<0||x>=h||y<0||y>=w) return 0;
if( visit[x][y]||mp[x][y]=='#') return 0;
return 1;
}
void dfs(int x,int y)
{
//访问
visit[x][y]=1;
//四个方向遍历
for(int i=0;i<4;i++)
{
int newx = x+X[i];
int newy = y+Y[i];
if(check(newx,newy)) dfs(newx,newy);
}
return ;
}
int main()
{
//freopen("input.txt","r",stdin);
while(cin>>w>>h&&w!=0&&h!=0)
{
memset(visit,0,sizeof(visit));
for(int i=0;i<h;i++)
{
for(int j=0;j<w;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='@')
{
qidianx=i;
qidiany=j;
}
}
}
dfs(qidianx,qidiany);
int ans =0;
//只有一个草丛,所有访问过的都是组成草丛的块
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
if(visit[i][j]) ans++;
cout<<ans<<endl;
return 0;
}