您的位置:首页 > 其它

算法总结—深度优先搜索DFS

2016-06-13 13:39 513 查看
深度优先搜索(DFS)

往往利用递归函数实现(隐式地使用栈)。

深度优先从最开始的状态出发,遍历所有可以到达的状态。由此可以对所有的状态进行操作,或列举出所有的状态。

1.poj2386LakeCouting

题意:八连通被认为连接在一起,求总共有多少个水洼?

SampleInput:

1012
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

SampleOutput

3

思路:从任意W开始,一次DFS把连通的.全部变为W,遍历图直到没有.为止,进行DFS次数即为水洼的个数。
代码:


#include<iostream>
usingnamespacestd;
intN,M;
charpond[100][100];//globalvariable
voiddfs(inti,intj){//注意参数设计,要不要返回值
pond[i][j]='.';
for(intdx=-1;dx<=1;++dx){//八连通遍历方式,四连通往往事先开好数组,见后续题目
for(intdy=-1;dy<=1;++dy){
intx=i+dx,y=j+dy;
if(x>=0&&x<N&&y>=0&&y<M&&pond[x][y]=='W'){
dfs(x,y);
}
}
}
return;
}
intmain(){
cin>>N>>M;
for(inti=0;i<N;++i){
for(intj=0;j<M;++j){
cin>>pond[i][j];
}
}

intcount=0;
for(inti=0;i<N;++i){
for(intj=0;j<M;++j){
if(pond[i][j]=='W'){
dfs(i,j);
count++;
}
}
}
cout<<count<<endl;
}


2.poj1979RedandBlack

题意:@表示起点,可以上下左右四方向走,"."为黑色,可以走,“#”为红色,不可以走,问可以到达多少个黑色位置?

SampleInput

69
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
119
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
116
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
77
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
00


SampleOutput

45
59
6
13

思路:从起点处DFS遍历即可,当前点为“.”则将其改为“#”,result++,并以该点出发继续遍历。
代码:


#include<iostream>
usingnamespacestd;
charrect[20][20];
intresult=0;//全局的result
intdx[4]={-1,0,0,1};//四连通处理方法
intdy[4]={0,1,-1,0};
intW=1,H=1;
voiddfs(intsx,intsy){
rect[sx][sy]='#';
result++;
for(inti=0;i<4;++i){
intx=sx+dx[i],y=sy+dy[i];
if(x>=0&&x<H&&y>=0&&y<W&&rect[x][y]=='.'){
dfs(x,y);
}
}
return;
}
intmain(){
while(W!=0&&H!=0){
cin>>W>>H;
if(W==0&&H==0){
return0;
}
intsx,sy;
for(inti=0;i<H;++i){
for(intj=0;j<W;++j){
cin>>rect[i][j];
if(rect[i][j]=='@'){
sx=i;
sy=j;
}
}
}
dfs(sx,sy);
cout<<result<<endl;
result=0;
}
return0;
}



3.aoj0118PropertyDistribution

题意:苹果是@,梨是#,蜜柑是*。四连通且相同品种在一个区域。计算每组数据区域的个数。

SampleInput:

1010
####*****@
@#@@@@#*#*
@##***@@@*
#****#*@**
##@*#@@*##
*@@@@*@@@#
***#@*@##*
*@@@*@@##@
*@*#*@##**
@****#@@#@
00

OutputfortheSampleInput

33

思路:类似第一题的池塘数个数的思路,DFS遍历,将遍历完毕的节点改为不同于上述三种标志的第四种标志,如“X”,
并且在DFS函数中加入标志参数用于判断同类水果区域,一次DFS,result++,当所有标志为X时,遍历结束。
代码:


#include<iostream>
usingnamespacestd;
chargarden[100][100];
intH=1,W=1;
intresult=0;
intdx[4]={-1,0,0,1};
intdy[4]={0,1,-1,0};
voiddfs(intsx,intsy,charc){//加入char判断是否同一类
garden[sx][sy]='x';
for(inti=0;i<4;++i){
intx=sx+dx[i],y=sy+dy[i];
if(x>=0&&x<H&&y>=0&&y<W&&garden[x][y]==c){
dfs(x,y,c);
}
}
return;
}
intmain(){
while(H!=0&&W!=0){
cin>>H>>W;
if(H==0&&W==0){
return0;
}
for(inti=0;i<H;++i){
for(intj=0;j<W;++j){
cin>>garden[i][j];
}
}
for(inti=0;i<H;++i){
for(intj=0;j<W;++j){
if(garden[i][j]!='x'){
dfs(i,j,garden[i][j]);
result++;
}
}
}
cout<<result<<endl;
result=0;
}
}


4.aoj0033Ball
题意:A管进球,B,C管出球,给定如球顺序,判断能否移动挡板,使得B,C出球顺序均为从下往上标号递增。


SampleInput

2
31425678910
10987654321

OutputfortheSampleInput

YES
NO

思路:DFS遍历,一个参数记录当前到第几个球,另外两个记录当前B,C顶端球得数值,以便于比较。
代码:


#include<iostream>
usingnamespacestd;
intA[10];
booldfs(intstart,inttopB,inttopC){//有返回值的DFS
if(start==9){//最后一个球啦
if(A[start]>topB||A[start]>topC){
returntrue;
}
else{
returnfalse;
}
}
boolb1=false,b2=false;
if(A[start]>topB){//B可以放,放进去试
b1=dfs(start+1,A[start],topC);
}
if(A[start]>topC){//C可以放,放进去试
b2=dfs(start+1,topB,A[start]);
}
return(b1||b2);//B,C都不可以放的时候,返回false,否则true
}
intmain(){
intN;
cin>>N;
for(inti=0;i<N;++i){
for(intj=0;j<10;++j){
cin>>A[j];
}
boolresult=dfs(0,0,0);
if(result==true){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
return0;
}


5.poj3009Curling2.0

题意:

可以沿上下左右走到障碍物,碰到障碍物后停下,障碍物消失,然后可以继续出发,出界算失败,超过十步算失败,问能否从S到G,

不能输出-1,能输出最少步数。(0路径,1障碍物,2起点,3终点)



SampleInput

21
32
66
100210
110000
000003
000000
100001
011111
61
112113
61
102113
121
201111111113
131
2011111111113
00

SampleOutput

1
4
-1
4
10
-1

思路:这种走到底的题目也可以用DFS,设计成无返回值,到达3后比较当前值与已有最小值的大小。注意走到底的写法(while)

代码:

#include<iostream>
#include<cstring>
#include<limits.h>
usingnamespacestd;
intboard[21][21];
intW=1,H=1;
intdx[4]={-1,0,0,1};
intdy[4]={0,1,-1,0};
intsx,sy,minStep=INT_MAX;
voiddfs(intsx,intsy,intstep){//设计成无返回值,当board[i][j]==3时比较当前step+1与最小的step并更新
if(step>=10){//剪枝
return;
}
for(inti=0;i<4;++i){
intx=sx+dx[i],y=sy+dy[i];
if(x>=0&&x<H&&y>=0&&y<W&&board[x][y]!=1){
while(x>=0&&x<H&&y>=0&&y<W&&board[x][y]!=1){//走到底的判断
if(board[x][y]==3){
if(step+1<minStep){
minStep=step+1;
break;
}
}
x+=dx[i];
y+=dy[i];
if(board[x][y]==1){
board[x][y]=0;
dfs(x-dx[i],y-dy[i],step+1);
board[x][y]=1;//恢复状态
}
}
}
}
return;
}

intmain(){
while(W!=0&&H!=0){
cin>>W>>H;
if(W==0&&H==0){
return0;
}
memset(board,0,sizeof(board));
for(inti=0;i<H;++i){
for(intj=0;j<W;++j){
cin>>board[i][j];
if(board[i][j]==2){
sx=i;
sy=j;
}
}
}
dfs(sx,sy,0);
if(minStep==INT_MAX){
cout<<"-1"<<endl;
}
else{
cout<<minStep<<endl;
}
minStep=INT_MAX;
}
return0;
}


6.poj1321棋盘问题

题意:n*n矩阵形状的棋盘(“#”为可摆放棋盘区域,“.”为不可摆放空白区域),要摆放k个棋子,同行同列不能有两个,共多少种方案?

SampleInput

21
#.
.#
44
...#
..#.
.#..
#...
-1-1

SampleOutput

2
1

思路:DFS的思路,一行一行的确定摆放位置,开一个数组place[8]记录哪一列已经有摆放。
注意k如果小于N时,可以有某一行不摆放元素,所以代码24行DFS(row+1,num)必须添加。

代码:


#include<iostream>
#include<cstring>
usingnamespacestd;
intN=1,K=1;
charchess[8][8];
intresult=0;
intplaced[8]={0};//记录该列是否有摆放
voiddfs(introw,intnum){
if(num==K){//摆放成功,方案数++
result++;
return;
}
if(row==N){
return;
}

for(inti=0;i<N;++i){
if(chess[row][i]=='#'&&placed[i]==0){
placed[i]=1;
dfs(row+1,num+1);
placed[i]=0;
}
}
dfs(row+1,num);//!容易忽略

}
intmain(){
while(N!=-1&&K!=-1){
cin>>N>>K;
if(N==-1&&K==-1){
return0;
}
memset(chess,0,sizeof(chess));
memset(placed,0,sizeof(placed));
for(inti=0;i<N;++i){
for(intj=0;j<N;++j){
cin>>chess[i][j];
}
}
dfs(0,0);
cout<<result<<endl;
result=0;
}

}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: