目录
1. 排列数字
题目描述
给定一个整数 nn,将数字 1∼n1∼n 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 nn。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤71≤n≤7
输入样例:
3
输出样例:
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 10;
int n;
int path[N];//记录路径
int st[N];//记录这个点是否已经走过
void dfs(int u){//搜索到了第u层
if(u==n){//此时第n-1层已经搜索完毕,输出这条路径
for(int i = 0;i<n;i++){
printf("%d ",path[i]);
}
printf("\n");
return;
}
//没有搜索完当前这条路的话就继续往下搜索
for(int i = 1;i<=n;i++){
if(!st[i]){//st不为1时表示这个点没有在当前路径走过,可以走
path[u] = i;//记录此层的路径点
st[i] = 1;//标记已经走过
dfs(u+1);//搜索下一个点
st[i] = 0;//这条路已经搜索完毕,需要回溯复原
}
}
}
int main(){
scanf("%d",&n);
dfs(0);//从第0层开始搜索
return 0;
}
题解分析
我们把全排列的各种可能看成多条路径,根据数字出现的位置分成不同层,从第0层开始搜索路径,搜索到第n-1层时表示找到了一条路径,同时回溯,去寻找下一条路径
代码中的注释已经解释了搜索时的思想
2.n-皇后问题
题目描述
n−n−皇后问题是指将 nn 个皇后放在 n×nn×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 nn,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数 nn。
输出格式
每个解决方案占 nn 行,每行输出一个长度为 nn 的字符串,用来表示完整的棋盘状态。
其中
.
表示某一个位置的方格状态为空,Q
表示某一个位置的方格上摆着皇后。每个方案输出完成后,输出一个空行。
注意:行末不能有多余空格。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤91≤n≤9
输入样例:
4
输出样例:
.Q.. ...Q Q... ..Q. ..Q. Q... ...Q .Q..
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 15;
int n;
char a[N][N];//棋盘 (同时用来记录方案)
int shu[N];//记录列
int zxie[N],yxie[N];//记录两条斜线
void dfs(int u){//搜索到了第u层 (行)
if(u==n){//此时最后一层已经搜索完毕
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
printf("%c",a[i][j]);
}
printf("\n");
}
printf("\n");
return;
}
for(int i = 0;i<n;i++){//此时i表示列
if(!shu[i]&&!zxie[i-u+n]&&!yxie[i+u]){//不在同一列且不在同一斜线上
a[u][i] = 'Q';
shu[i]=zxie[i-u+n]=yxie[i+u]=1;//标记列和斜线
dfs(u+1);//继续搜索下一层(行)
a[u][i] = '.';//此时一个方案已经搜索完成,要回溯,以防影响下一个方案的搜索
shu[i]=zxie[i-u+n]=yxie[i+u]=0;
}
}
}
int main(){
scanf("%d",&n);
for(int i = 0;i<n;i++){//初始化棋盘
for(int j = 0;j<n;j++){
a[i][j] = '.';
}
}
dfs(0);//从第0层开始
return 0;
}
题解分析
题目要求任意两个皇后都不能处于同一行、同一列或同一斜线上,我们就将思维转换为每一行上只能放置且一定要放置一个皇后,于是我们只需要设置同一列和同一斜线的冲突
同一列:shu[N],当第i列有皇后是shu[i]为1,没有时shu[i]为0,此时这一列还可以放置皇后
同一斜线:(我们已经知道了代码中的u表示行,i表示列)
如图所示,两种斜线的行与列都有一个固定的关系,当在同一条斜线上时,显然,b是固定的,我们以此关系作为判断同一斜线的依据,注意,u-i可能小于0,以防数组越界,我们将u-i改写成u-i+n
思路分析如代码中注释
3.八皇后问题
题目描述
描述
在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。
输入
无输入。
输出
按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。
样例输入
无样例输出
No. 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 No. 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 No. 3 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 No. 4 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 No. 5 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 No. 6 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 No. 7 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 No. 8 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 No. 9 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 ...以下省略
代码
#include <iostream>
#include <cstdio>
using namespace std;
//此样例行列转置了
int count = 0;//记录方案数
const int N = 10;
int n = 8;
int a[N][N];//棋盘(同时记录方案)
int shu[N],zxie[N],yxie[N];//记录列与斜线
void dfs(int u){//此时搜索到了第u层(行)
if(u==n){//搜索完成了最后一层,一个方案
count++;
printf("No. %d\n",count);
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
printf("%d ",a[i][j]);
}
printf("\n");
}
return;
}
//没有搜索到最后一层继续搜索
for(int i = 0;i<n;i++){//此时i代表列
if(!shu[i]&&!zxie[u-i+n]&&!yxie[u+i]){//没有在同一列同一斜线上
shu[i]=zxie[u-i+n]=yxie[u+i]=1;//标记已走过
a[i][u] = 1;//放置皇后
dfs(u+1);//搜索下一层
shu[i]=zxie[u-i+n]=yxie[u+i]=0;//回溯
a[i][u] = 0;
}
}
}
int main(){
dfs(0);//从第0层开始搜索
return 0;
}
题解分析
这个题做法与上一题没什么本质区别,思路注释中也写得很清楚了
唯一要注意的是:(题目中的一句话)
按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。
如果按平常的dfs思路写出来,我们会发现自己的输出与样例的输出方案的顺序不一样,那么就要注意题目的这句话了,经过观察与尝试,发现 :
此样例格式的行与列进行了转置(行变列,列变行),于是输出时要先输出原来的列,再输出原来的行
还可能出现的错误:
此样例的No.num建议直接复制,不然可能会有符号错误问题
4.踩方格
题目描述
描述
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:
a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;
b. 走过的格子立即塌陷无法再走第二次;
c. 只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。输入
允许在方格上行走的步数n(n <= 20)
输出
计算出的方案数量
样例输入
2
样例输出
7
代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int a[50][25];//方格模型(同时作为标记模型)
int dfs(int x,int y,int step){
if(step==n+1){
return 1;
}
int count = 0;
if(!a[x+1][y]){//向东走
a[x][y] = 1;//标记当前路径已走过
count += dfs(x+1,y,step+1);
a[x][y] = 0;//回溯
}
if(!a[x-1][y]){//向西走
a[x][y] = 1;
count += dfs(x-1,y,step+1);
a[x][y] = 0;
}
if(!a[x][y+1]){//向北走
a[x][y] = 1;
count += dfs(x,y+1,step+1);
a[x][y] = 0;
}
return count;
}
int main(){
scanf("%d",&n);
printf("%d",dfs(25,0,1));
return 0;
}
题解分析
向北东西三个方向进行深度优先搜索,思路如注释,注意点:
1.方格模型a[50][25],这样设置是为了防止数组越界,因为东西最多各可走20格,北最多可走20格,南不可走,故得二维数组大小
2.计算方案数的count不能作为全局变量而作为dfs函数中变量的原因,我们的思路是总方案数等于向北东西三个不同方向的方案数的总和,应当在这一层次的调用中归为0,接下来的函数递归调用也是如此(相当于回溯),如果作为全局变量归为0,那么会重复计算各个层次函数调用的方案数
5.棋盘问题
题目描述
描述
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。
输入
输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。输出
对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
样例输入
2 1 #. .# 4 4 ...# ..#. .#.. #... -1 -1
样例输出
2 1
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 10;
int n,k;
int x;//放置的棋子数
long long count;//方案数
char a[N][N];//棋盘
int shu[N];//标记列
void dfs(int u){//搜索到了第u行(层)
if(x==k){//已经摆放了k个棋子
count++;
return;
}
if(u==n){
return;
}
for(int i = 0;i<n;i++){//此时i表示列
if(!shu[i]&&a[u][i]=='#'){//如果此列无冲突,就放置棋子
x++;
shu[i] = 1;//标记已放棋子的列
dfs(u+1);//搜索下一层(行)
shu[i] = 0;//回溯
x--;
}
}
dfs(u+1);
}
int main(){
while(1){
scanf("%d%d",&n,&k);
getchar();
if(n==-1&&k==-1){
break;
}
for(int i = 0;i<n;i++){//输入棋盘
for(int j = 0;j<n;j++){
scanf("%c",&a[i][j]);
}
getchar();
}
count = 0;
x = 0;
dfs(0);//从第0层开始搜索
printf("%lld\n",count);
}
return 0;
}
题解分析
这个题目更n-皇后问题思想基本一样,但又有一点点不同,不同之处在于,n-皇后问题中的k=n,而这个题目中的k<=n,也就是说,不再是每一行都一定要有一个皇后,但是每一行最多能放一个皇后。
增加的代码就是第27行的dfs(u+1)了,也就是说,当n=k时我们只需要从第0行搜索到第n-1行即可,当n<=k时,我们需要从第 0到第n-k-1行开始搜索到第n-1行,当然第27 行的代码表示从第0行到第n-1行开始搜索到第n-1行,这取决于k的大小,所以当搜索到第n-1行时,我们在第15行到第17行设置了一个return;,防止因开始行数的太大而造成函数无穷调用。
还有一个注意点,第11行到第14行与第15行到第17行的代码段不能互换位置,为什么呢?
因为当我们搜索到第n-1行时,如果已经搜到了k个位置,我们要算它作为一个方案,而不是直接返回
最后,算是用这些题目来初识dfs吧!