算法介绍
作为搜索算法的一种,DFS对于寻找一个解的NP(包括NPC)问题作用很大。但是,搜索算法毕竟是时间复杂度是O(n!)的阶乘级算法,它的效率非常低,在数据规模变大时,这种算法就显得力不从心了。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
总结来说就是,这个算法运用的时候就是找一个头结点,然后沿着这个头结点一直找下去,直到走到最后一个满足条件的分节点,然后再寻找另一条路径,当沿着一条路走不满足条件时会自动的跳入上一层节点进行判断。dfs算法通常与回溯算法一起使用。
代码框架
void dfs(主要控制参数)
{
if(参数达到临界条件)
return; //终止
if(判定当前方案合法或还没有被标记)
{
标记当前位置;
dfs(下一位置);
还原位置;//回溯
}
}
案例
1、输出1~n的全排列
#include <iostream>
using namespace std;
int a[100]={
0}; //用于存放待存入的数
int vis[100]={
0};
int n;
void dfs(int step)
{
if(step == n+1)
{
for(int i=1;i<=n;i++)
cout<< a[i] << " ";
cout << endl;
return;
}
for(int i=1;i<=n;i++)
{
if(vis[i]==0) //判断i是否已经被填到数组a中了
{
a[step]=i;
vis[i]=1; //标记i被填入
dfs(step+1); //填写下一个
vis[i]=0; //回溯,寻找其他解
}
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
2、迷宫问题<1>
题目:某人掉进了一个迷宫里面,请你告诉他是否有机会逃出去,第一上输入两个整数n和m表示一个n×m的迷宫。接下来的输入一个n行m列的迷宫。其中‘S’表示该人的起始位置,‘*’表示墙,无法通过,’.'表示路,可以通过,‘T’表示迷宫的出口,如果可以逃出去输出yes,否则输出no。
1<=m,n<=10;
输入格式:
3 4
S**.
…
***T
输出格式:
yes
#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int flag=0;
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
int m,n;
int q,p;
void dfs(int x,int y)
{
if(a[x][y]=='T')
{
flag=1;
return;
}
for(int i=0;i<=3;i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx>=1&&tx<=m&&ty>=1&&ty<=n&&a[tx][ty]!='*'&&vis[tx][ty]==0)
{
vis[tx][ty]=1;
dfs(tx,ty);
vis[tx][ty]=0;
}
}
}
int main()
{
while(cin >> m >> n)
{
memset(vis,0,sizeof(vis));
flag=0;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin >> a[i][j];
if(a[i][j]=='S')
{
p=i;
q=j;
}
}
}
dfs(p,q);
if(flag==1)
{
cout << "yes" << endl;
}
else if(flag==0)
{
cout << "no" << endl;
}
}
return 0;
}
3、迷宫问题<2>进阶
题目:某人掉进了一个迷宫里面,请你告诉他是否有机会逃出去,第一上输入两个整数n和m表示一个n×m的迷宫。接下来的输入一个n行m列的迷宫。其中‘S’表示该人的起始位置,‘*’表示墙,无法通过,’.'表示路,可以通过,‘T’表示迷宫的出口,如果可以逃出去输出逃出去的最少步数step,若不能输出-1
#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int flag=0;
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
int m,n;
int q,p;
int ans=9999999999;
void dfs(int x,int y,int step)
{
if(step>ans)
{
return;
}
if(a[x][y]=='T')
{
flag=1;
if(ans>step)
{
ans=step;
}
return;
}
for(int i=0;i<=3;i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx>=1&&tx<=m&&ty>=1&&ty<=n&&a[tx][ty]!='*'&&vis[tx][ty]==0)
{
vis[tx][ty]=1;
dfs(tx,ty,step+1);
vis[tx][ty]=0;
}
}
}
int main()
{
cin >> m >> n;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin >> a[i][j];
if(a[i][j]=='S')
{
p=i;
q=j;
}
}
}
dfs(p,q,0);
if(flag==1)
{
cout << ans<< endl;
}
else if(flag==0)
{
cout << -1 << endl;
}
return 0;
}
4、红与黑----变化
题目:蒜厂有一间长方形的房子,地面上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。
请写一个程序,计算你总共能够到达多少块黑瓷砖。
输入格式:第一行是两个整数W和H,分别表示x方向和y方向瓷砖的数量。W和H都超不过20。
在接下来的H行中,每行包括W个字符。每个字符表示一块瓷砖的颜色,规则如下
1)’.’:黑色的瓷砖
2)’#‘:白色的瓷砖
3)’@‘:黑色的瓷砖,并且你站在这块瓷砖上。该字符在每个数据集合 中唯一出现一次。
输出格式:输出一行,显示从起始位置出发能到达的瓷砖数(计数时包括起始位置)
#include <bits/stdc++.h>
using namespace std;
char a[50][50];
int vis[50][50];
int dir[4][2]={
1,0,-1,0,0,1,0,-1};
int w,h;
int q,p;
int ans;
void dfs(int x,int y)
{
for(int i=0;i<=3;i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx>=1&&tx<=h&&ty>=1&&ty<=w&&a[tx][ty]=='.'&&vis[tx][ty]==0)
{
ans++;
vis[tx][ty]=1;
dfs(tx,ty);
}
}
}
int main()
{
while(cin >>w>>h)
{
ans=1;
memset(vis,0,sizeof(vis));
for(int i=1;i<=h;i++)
{
for(int j=1;j<=w;j++)
{
cin >> a[i][j];
if(a[i][j]=='@')
{
p=i;
q=j;
}
}
}
dfs(p,q);
cout << ans << endl;
}
return 0;
}
5、求最短路径
假设 一张地图 N行M列 ,0表示通畅1表示障碍物,P,Q表示终点坐标.求点(0,0)到(P,Q)最短路径
输入:
p q
m n
地图
输出:
路径
最短步数
#include <bits/stdc++.h>
using namespace std;
int ans=99999999999;
int a[50][50]={
0}; //地图
int temp[50][50]={
0}; //判断是否已经走过了
int p,q; //终止位置
int m,n;//大小
int dir[4][2]={
{
0,1},{
0,-1},{
1,0},{
-1,0}}; //向哪个方向走
void dfs(int x,int y,int step)
{
if(x==p-1&&y==q-1)
{
if(step<=ans)
{
ans=step; //如果路径更小则更换
}
return;
}
for(int i=0;i<4;i++)
{
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(tx>=0&&tx<=m-1&&ty>=0&&ty<=n-1&&a[tx][ty]==0&&temp[tx][ty]==0) //约束条件:保证在地图里边,保证该方向能走,保证没有来过这个地方
{
temp[tx][ty]=1;
cout << dir[i][0] << dir[i][1] << step+1 << endl; //输出所走的路径
dfs(tx,ty,step+1);
temp[tx][ty]=0;
}
}
}
int main()
{
cin >> p >> q;
cin >> m >> n;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin >> a[i][j];
}
}
dfs(0,0,0);
cout << ans << endl;
return 0;
}
6、八皇后问题
详情
该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
#include <bits/stdc++.h>
using namespace std;
int place[8]={
0};
int d1[15];
int d2[15];
memset(d1,1,sizeof(d1));
memset(d2,1,sizeof(d2));
int flag[8];
memset(flag,1,sizeof(flag));
int nums=0;
void dfs(int n)
{
if(n==8)
{
nums++;
}
int col;
for(col=0;col<8;col++)
{
if(flag[col]&&d1[n-col+7]&&d2[n+col]) //判断该位置是否可以放置皇后
{
place[n]=col;
flag[col]=false;
d1[n-col+7]=false;
d2[n+col]=false;
dfs(index+1);
//回溯,寻找其他可行组
flag[col]=true;
d1[n-col+7]=true;
d2[n+col]=true;
}
}
}
int main()
{
dfs(0);
cout << nums << endl;
return 0;
}