DFS的大概模板
void dfs(int x)//关于传入参数问题,根据题意而定,看在题目运行的过程中,哪些是在变得
{
if(满足输出条件)
{
输出解;
return ;
}
if(目前已经没有必要进行下去的条件){
return ;
}//剪枝操作
//如果传入的条件,还需要继续搜下去,分析每一种情况后面跟哪些情况,然后循环,每个情况(注意前提:得符合题意)都深搜一下
for(int i=1;i<=每种情况数;i++)
if(满足进一步搜索条件)//判断是否合理
{
为进一步搜索所需要的状态打上标记;//是需要的状态
dfs(t+1);
恢复到打标记前的状态;//也就是说的{回溯一步}
}
}
}
拿最经典的八皇后问题解释dfs,正常情况下,如果有一个6*6的棋盘,在第一行,选择落子,不考虑规则限制,我们有6种落子方式。
第二行,有1 2 3 4 5 6,6种落子方式。以此类推,6的6次方种情况。
但现在由于规则限制,跟bfs一样根据流程想出一个树出来。指需要按照规则取舍一下那些结点有,哪些没有即可,搜索方式上采取递归+回溯
从最终结果上,呈现出来的是从根节点一条路走到底,然后撤回底点的上一个结点,撤回后能走另一个,就走另一个
DFS传入的参数一定要能表示出每个情况的状态
例题1
P1219 八皇后 题目链接
一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n \times nn×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
说明/提示
【数据范围】
对于 100%100% 的数据,6 ≤n≤13。
思路
就是深搜加回溯,技巧就是对于斜对角线上面是否有结点的判别,算一下就知道,左下到右上斜对角线i+j=固定值,左上到右下i-j+n=固定值
按照深搜的思想进行分析,在题目运行过程中,所脑补出的树形结构,什么在改变。开始一个棋盘上面,什么都没放,在第一行上可以放1 2 3 4 5 6位置。
如果选取1,对1深搜结果如下,选择2,3都一样。
下一步,第二行,1已经被放过,可以放2 3 4 5 6,放2位置不行,3位置可以
下一步,从3行2列开始。。。。。。。
过程中可以发现,改变是行数,第一行每个可能深搜第二行,第二行每个可能深搜第三行。
AC代码
#include<bits/stdc++.h>
using namespace std;
int p[100];//储存路径
int visy[100]={0};//记录列是否访问
int vxie1[100]={0};//记录左下到右上斜线是否访问
int vxie2[100]={0};//左上到右下
int n;//n*n棋盘
int total=0;//记录搜索到结果总数
void dfs(int x){
if(x==n){
if(total<=2)
for(int i=0;i<n;i++) printf("%d ",p[i]+1);
if(total<2) printf("\n");
total++;
return ;
}//如果X=n表示已经搜到最后一行,total++,一条路走到尽头
//否则这一行,有n种情况,如果符合题意,每个情况继续深搜
for(int j=0;j<n;j++){
if(!visy[j]&&!vxie1[x+j]&&!vxie2[x-j+n]){
p[x]=j;//储存路径,表示x行选择j列
visy[j]=1;vxie1[x+j]=1;vxie2[x-j+n]=1;//标记这一行,列,斜线
dfs(x+1);//深搜下一行
visy[j]=0;vxie1[x+j]=0;vxie2[x-j+n]=0;//深搜结束
//回到这一行初始状态就是回溯,进行下一种情况深搜
}
}
}
int main(){
cin>>n;
dfs(0);
printf("\n%d",total);
}
例题2
试题 历届试题 分考场
http://lx.lanqiao.cn/problem.page?gpid=T457
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
n个人参加某项特殊考试。
为了公平,要求任何两个认识的人不能分在同一个考场。
求是少需要分几个考场才能满足条件。
输入格式
第一行,一个整数n(1<n<100),表示参加考试的人数。
第二行,一个整数m,表示接下来有m行数据
以下m行每行的格式为:两个整数a,b,用空格分开 (1<=a,b<=n) 表示第a个人与第b个人认识。
输出格式
一行一个整数,表示最少分几个考场。
样例输入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
样例输出
4
样例输入
5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
样例输出
5
思路
这题因为我比较笨,所以想了很久才想通。同样是分析题目进行过程脑补出树或图。
第一步。所有房间都是空的,放里面放人,放第一个人
第二步。放第二个人,如果与第一个房间所有人没关系,放第一个房间,否则新开一个房间,放进去。
第三步,放第三个人,如果1,2房间不能放,开一个房间放到3号房间,否则放在1或2房间
第四步。。。。
分析过程可以看出,每次房间数是在变化,每次放的人的编号也在变化。意思就是树的每个点,都包含我现在放入编号几,我用了多少个房间,在每个点里搜出最佳点。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,m,min_num;
int mp[102][102]={{0}};
int r[102][102]={{0}};
void dfs(int x,int num){//x表示开始安排编号X,已经用了num个房间
if(num>=min_num) return ;//剪枝
if(x>n){
if(num<min_num) min_num=num;
}//如果已经安排完,比较后更新最少房间数
//八皇后一样到x行,都会有n种情况。对于此题到达分配编号x,有num个房间
for(int i=1;i<=num;i++){
int k=0;
while(r[i][k]&&!mp[x][r[i][k]]) k++;//找到放在房间的那一个位置
if(r[i][k]==0){
r[i][k]=x;
dfs(x+1,num);
r[i][k]=0;
}
}//找不到合适的房间,就开一个新的房间继续深搜,代码就是把流程写了一遍
r[num+1][0]=x;
dfs(x+1,num+1);
r[num+1][0]=0;
}
int main()
{
cin>>n>>m;
min_num=n;
while(m--){
int x,y;
cin>>x>>y;
mp[x][y]=mp[y][x]=1;
}
dfs(1,0);
printf("%d\n",min_num);
}
例题3
P1605 迷宫题目地址
题目背景
给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。
题目描述
无
输入格式
第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。
输出格式
给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。
输入输出样例
输入 #1 复制
2 2 1
1 1 2 2
1 2
输出 #1 复制
1
说明/提示
【数据规模】
1≤N,M≤5
思路
这道题也可以用bfs做,每个结点有四处走向,不断地扩散,找到终点,返回
但是记录能有几条路比较麻烦
用dfs思路和前两题一样,按照题目过程脑补出树
第一步,在起点,有4种情况,如果可以,上下左右都深搜一下(如果选择深搜右)
第二步,起点的右节点为起点,上下左右如果可以的话,都深搜(如果可以还选择右)
第三步,起点的右的右,上下左右,都深搜,。。。。还选右的话
如果题意允许,就一直讲走完到了终点,递归出口让total++就行
AC代码
就没有注释太多了,跟前面差不多
#include<bits/stdc++.h>
using namespace std;
int n,m,t,total=0;//n行,m列,t个障碍物
int sx,sy,x2,y2;//起点x,y,终点x,y
int mp[10][10]={{0}};
int vis[10][10]={{0}};
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
void dfs(int x,int y){
if(x==x2&&y==y2){
total++;
return ;
}
for(int i=0;i<4;i++){
int xx=x+dir[i][0];
int yy=y+dir[i][1];
if(!vis[xx][yy]&&xx>=1&&xx<=n&&yy>=1&&yy<=m&&!mp[xx][yy]){
vis[x][y]=1;
dfs(xx,yy);
vis[x][y]=0;
}
}
}
int main(){
cin>>n>>m>>t;
cin>>sx>>sy>>x2>>y2;
for(int i=0;i<t;i++){
int zx,zy;
cin>>zx>>zy;
mp[zx][zy]=1;
}
dfs(sx,sy);
printf("%d\n",total);
}
总结完毕!
来源:CSDN
作者:坚持难能可贵
链接:https://blog.csdn.net/weixin_45559159/article/details/104187523