1. 全排列问题
#include<iostream>
using namespace std;
const int N=10;
int n;
int path[N];//存储
bool st[N];//状态数组
void dfs(int u)
{
if(u==n)//递归到最后一个数字
{
for(int i=0;i<n;i++) printf("%d ",path[i]);//将路径打印出来
puts("");
return;
}
for(int i=1;i<=n;i++)
{
if(!st[i])//没有被用过的数
{
path[u]=i;//将此数存下
st[i]=true;//并标记此数用过了
dfs(u+1);//下一层
st[i]=false;//恢复现场
}
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
//当然也可以使用next_permutation
相反还有prev_permutation
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int a[10];
int n;
cin >> n;
for(int i = 1; i<= n; i++) {
a[i] = i;
}
do {
for(int i = 1; i <= n; i++) {
cout << " " <<a[i];
}
cout << endl;
}while(next_permutation(a + 1, a + n + 1));
return 0;
}
对于数字全排列另外一种写法:
#include<stdio.h>
#include<iostream>
#include<algorithm>
int a[10];
using namespace std;
int main()
{
int n = 0;
cin >> n;
int j = 1;
for (int i = 1; i <= n; i++)
{
a[i] = n - i + 1;//a[i]最开始为3 2 1
j *= i;//共有多少个数
}
for (int i = 1; i <= j; i++)
{
next_permutation(a + 1, a + n + 1);
for (int k = 1; k <= n; k++)
{
cout << " " << a[k];
}
cout << endl;
}
return 0;
}
对于字符串的全排列:
#include <bits/stdc++.h>
using namespace std;
string s;
int main()
{
cin >> s;
do {
cout << s << endl;
} while(next_permutation(s.begin(), s.end()));
return 0;
}
2. n皇后问题
n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
#include<iostream>
using namespace std;
const int N=20;//防止在udg中溢出
int n;
char g[N][N];
bool col[N],dg[N],udg[N];
void dfs(int u)
{
if(u==n)//放完了
{
for(int i=0;i<n;i++) puts(g[i]);//一行一行打印
puts("");
return;
}
for(int i=0;i<n;i++)
{
if(!col[i]&&!dg[u+i]&&!udg[n-u+i])//判断列与对角线
{//在同一对角线上u+i会是同一个值,-u+i会也是同一个值,此处加一个n是为了保证为正
//类似截距 y=x+b;b=y-x; y=-x+b;b=x+y;
g[u][i]='Q';//放置皇后
col[i]=dg[u+i]=udg[n-u+i]=true;//表示此列/此对角线已经用过了
dfs(u+1);//下一层
col[i]=dg[u+i]=udg[n-u+i]=false;
g[u][i]='.';//恢复现场(因为一开始都是.,所以要恢复为原样)
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
g[i][j]='.'; //初始化
}
dfs(0);
return 0;
}
思考:
- n皇后问题中的n至少要4,小于4无法完成
- 因为是从上往下放,所以只需要考虑两条对角线和是否同列,不需要考虑同行。 其中i就相当于y竖着 u就相当于x
这里放一个只记录皇后列号的代码(不含具体方案):
#include<iostream>
using namespace std;
const int N=20;
int n;
int ans[N];
bool col[N],dg[N],udg[N];
void dfs(int u)
{
if(u==n+1)
{
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
cout<<endl;
return ;
}
for(int i=1;i<=n;i++)
{
if(!col[i]&&!dg[u+i]&&!udg[n-u+i])
{
ans[u]=i;
col[i]=dg[u+i]=udg[n-u+i]=true;
dfs(u+1);
col[i]=dg[u+i]=udg[n-u+i]=false;
}
}
}
int main()
{
cin>>n;
if(n<=3||n>10)
{
printf("no solute!");
return 0;
}
dfs(1);
return 0;
}
3. 递归实现指数型枚举
从 1∼n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
题目链接
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=16;
int n;
int st[N];// 状态,记录每个位置当前的状态:0表示还没考虑,1表示选它,2表示不选它
void dfs(int u)
{
if(u>n)
{
for(int i=1;i<=n;i++)
if(st[i]==1)//已选
printf("%d ",i);
printf("\n");
return;
}
st[u]=2;//不选
dfs(u+1);
st[u]=0;//恢复
st[u]=1;//选
dfs(u+1);
st[u]=0;
}
int main()
{
scanf("%d",&n);
dfs(1);
return 0;
}
4. 递归实现组合型枚举
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=30;
int n,m;//n个整数中随机选出 m 个
int way[N];
void dfs(int u,int start)//u为当前在选的第几个数,start则表示从几开始
{
if(u+n-start<m) return ;//剪枝 如果当前比如为u=1,1+5-4<3 那么4 5 肯定不满足
if(u==m+1)//选完了
{
for(int i=1;i<=m;i++) printf("%d ",way[i]);
puts("");
return ;
}
for(int i=start;i<=n;i++)
{
way[u]=i;
dfs(u+1,i+1);
way[u]=0;//恢复现场 (用0标记)
}
}
int main()
{
scanf("%d%d",&n,&m);
dfs(1,1);
return 0;
}
5. 数水坑
**写法1:**
#include<iostream>
#include<cstdio>
using namespace std;
int fxx[8]={-1,-1,-1,0,0,1,1,1};
int fxy[8]={-1,0,1,-1,1,-1,0,1};
int n,m,ans;
char a[105][105];
void dfs(int x,int y)
{
int dx,dy;
a[x][y]='.';
for (int i=0;i<8;i++)
{
dx=x+fxx[i];
dy=y+fxy[i];
if (dx>=1&&dx<=n&&dy>=1&&dy<=m&&a[dx][dy]=='W')
dfs(dx,dy);
}
}
int main()
{
scanf("%d %d\n",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
cin>>a[i][j];
}
ans=0;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (a[i][j]=='W')
{
ans++;
dfs(i,j);
}
}
}
printf("%d\n",ans);
return 0;
}
**写法2:**
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,ans;
char a[105][105];
void dfs(int x,int y)
{
int dx,dy;
a[x][y]='.';
for(int fx=-1;fx<=1;fx++)
{
for(int fy=-1;fy<=1;fy++)
{
dx=x+fx;
dy=y+fy;
if (dx>=1&&dx<=n&&dy>=1&&dy<=m&&a[dx][dy]=='W')
dfs(dx,dy);
}
}
}
int main()
{
scanf("%d %d\n",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
cin>>a[i][j];
}
ans=0;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (a[i][j]=='W')
{
ans++;
dfs(i,j);
}
}
}
printf("%d\n",ans);
return 0;
}
与数水坑十分类似的题目:
题目链接
此题答案各位小可爱自行寻找。
6. 打死我也不说
对于给定的矩阵,请判断其中是否藏有“DSWYBS”,如果有,给出首末两个字母的下标并计算密码;如果没有,打印一行“DSWYBS”。
注意:
若藏有“DSWYBS”,则这串字母必是沿行、列或斜45度方向依次排列的。
题目保证输入的矩阵至多藏有一串“DSWYBS”。
矩阵左上角下标为(0,0)。
区分大小写。
题目保证输入的矩阵不含有类似这样的排列(即:仅有DSWYB五个字母,但是字母B与S相邻,可组成DSWYBS的排列 ) :
DSB
WY
输入格式:
第一行给出两个整数M、N(均不大于15、不小于4),接下来M行,每行有N个字母或数字,以换行结束。输出格式:
如果输入矩阵中藏有“DSWYBS”,则输出三行,第一行和第二行分别是首字母D的下标和末字母S的下标,先行下标后列下标,以一个空格间隔。第三行给出两个字母四项下标值之和。
如果没藏有该串字母,则打印一行“DSWYBS”
输入样例1:
8 10
0x00z000d0
00aD00s000
00b0SWk000
000wcY000s
00000B0000
0000S00000
0000000000
0000000000
输出样例1:
1 3
5 4
13
输入样例2:
5 5
12345
adswa
54321
dswys
aaaaa
输出样例2:
DSWYBS
#include<stdio.h>
char arr[16][16];
int dx,dy;
int sx,sy,mx,my;
int flag=0;
int m,n;
char ans[]="DSWYBS";
int fx[8][2]={{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
void dfs(int x,int y,int step)
{
if(step==6)
{
sx=x;
sy=y;
flag=1;
return;
}
for(int i=0;i<8;i++)
{
dx=x+fx[i][0];
dy=y+fx[i][1];
if(dx>=0&&dx<m&&dy>=0&&dy<n&&arr[dx][dy]==ans[step])
{
dfs(dx,dy,step+1);
}
}
}
int main()
{
scanf("%d %d",&m,&n);
getchar();
for(int l=0;l<m;l++)
{
for(int k=0;k<n;k++)
{
scanf("%c",&arr[l][k]);
}
getchar();
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(arr[i][j]=='D')
dfs(i,j,1);
if(flag)
{
mx=i;
my=j;
break;
}
}
if(flag)
break;
}
if(flag)
{
printf("%d %d\n",mx,my);
printf("%d %d\n",sx,sy);
printf("%d",mx+my+sx+sy);
}
else
{
printf("DSWYBS");
}
return 0
}
7. 字母顺序归位
字符串顺序被打乱了
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int ans=1000;
string s,a="swpuacmnb";
void dfs(int deep,string p)
{
if(deep>9)
return ;
if(p==a)
{
ans=min(ans,deep);
return ;
}
for(int i=0;i<9;i++)
{
if(p[i]!=a[i])
{
for(int j=i+1;j<9;j++)
{
if(p[j]==a[i])
{
swap(p[j],p[i]);
dfs(deep+1,p);
swap(p[i],p[j]);
}
}
}
}
}
int main()
{
cin>>s;
dfs(0,s);
cout<<ans;
return 0;
}
8. 不同路径数
set+dfs:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 10;
int n, m, k;
int g[N][N];
set<int> S;//可以保证S中的数都不同
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void dfs(int x, int y, int u, int num)
{
if (u == k) S.insert(num);//达到k步
else
{
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < n && b >= 0 && b < m)
dfs(a, b, u + 1, num * 10 + g[a][b]);
}
}
}
int main()
{
//输入:
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
scanf("%d", &g[i][j]);
//每一个点都dfs一次
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
dfs(i, j, 0, g[i][j]);
printf("%d\n", S.size());
return 0;
}