您的位置:首页 > 其它

DFS&BFS专题(一)

2017-07-30 23:13 369 查看
题目列表

1.POJ-1321(简单DFS)

2.POJ-2251(三维坐标BFS)

3.POJ-1426(DFSf构造)

4.POJ-3087(BFS模拟)

5.POJ-3414(BFS模拟+记录路径)

6.UVA-11624(两次BFS)

7.POJ-3984(简单DFS&BFS都可以,记录路径)

8.HDU-1241(DFS种子填充)

9.HDU-1495(BFS求最短路)

10.HDU - 2612 (两次BFS)

Q1:POJ - 1321

题意:中文题

思路:

DFS的参数是从第i行往下搜索,设置num表示目前已经放好的棋子,如果num==k,放置的种数加1,如果递归层数大于n直接返回结束递归

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n, k;
char Map[10][10];
int v[10];
int sum, num;
void dfs(int x)
{
if(num == k){sum++; return;}
if(x > n)return;
for(int i = 0; i < n; i++)
if(Map[x][i] == '#' && !v[i])
{
v[i] = 1;
num ++;
dfs(x + 1);
num--;
v[i] = 0;
}
dfs(x + 1);
}
int main()
{
while(cin >> n >> k)
{
memset(Map, 0, sizeof(Map));
if(n == -1 && k == -1)break;
for(int i = 0; i < n; i++)cin >> Map[i];
memset(v, 0, sizeof(v));
sum = num =0;
dfs(0);
cout<<sum<<endl;
}
}


Q2:POJ-2251

题意:

在一个立体空间, 输入三个数,L,R,C,代表有L个平面,R行,C列,.代表可以走,#代表不能走,S代表开始点,E代表结束点,问从S开始走,对每个位置,有六个走法,即空间的六个方向的走法(上下东南西北),一分钟可以走一个点,问从S走到E点,最少可以经过多少分钟,若不能到达,则输出Trapped!

思路:

简单BFS运用,只是把普通的二维平面变成了三维立体,由四个方向变成了六个方向

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
char Map[30][30][30];
int visit[30][30][30];
int dir[6][3]={1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1};
struct node
{
int l,x,y,s;
node(int ll,int xx,int yy,int ss):l(ll),x(xx),y(yy),s(ss){}
};
int main()
{
int l,x,y;
int s1,s2,s3,c1,c2,c3;
while(cin>>l>>x>>y)
{
if(!l&&!x&&!y)break;
memset(visit,0,sizeof(visit));
memset(Map,0,sizeof(Map));
for(int i=0;i<l;i++)
{
for(int j=0;j<x;j++)cin>>Map[i][j];
}
for(int i=0;i<l;i++)
for(int j=0;j<x;j++)
for(int k=0;k<y;k++)
{
if(Map[i][j][k]=='S')
{
s1=i;s2=j;s3=k;
}
if(Map[i][j][k]=='E')
{
c1=i;c2=j;c3=k;
}
}
queue<node>q;
q.push(node(s1,s2,s3,0));
visit[s1][s2][s3]=1;
for(;;)
{
if(q.empty())
{
cout<<"Trapped!"<<endl;
break;
}
node a=q.front();
//cout<<a.l<<" "<<a.x<<" "<<a.y<<endl;
if(a.l==c1&&a.x==c2&&a.y==c3)
{
cout<<"Escaped in "<<a.s<<" minute(s)."<<endl;
break;
}
for(int i=0;i<6;i++)
{

4000
int cnt1=a.l+dir[i][0];
int cnt2=a.x+dir[i][1];
int cnt3=a.y+dir[i][2];
if(cnt1>=0&&cnt1<l&&cnt2>=0&&cnt2<x&&cnt3>=0&&cnt3<y&&!visit[cnt1][cnt2][cnt3]&&Map[cnt1][cnt2][cnt3]!='#')
{
visit[cnt1][cnt2][cnt3]=1;
q.push(node(cnt1,cnt2,cnt3,a.s+1));
}
}
q.pop();
}
}
return 0;
}


Q3:POJ-1426

题意:

给你一个[1,200]之间的整数n,要你求它的一个非0倍数m,这个m的十进制数只包含0和1.m不超过100位.

思路:

由于本题的数只有0和1构成,自然就想到了自己构造这个符合要求的数。所以这里我用DFS来构造这个数。虽然本题说最终结果数<=100位,但是经过测试本题测试数据中需要构造的数在unsigned long long 范围内有解,所以下面我用unsigned long long来表示需要构造的数。(并没有用高精度大整数)

DFS构造过程很简单了,就是每层循环向个位添加一个0或1即可。但是记得如果找到了一个合法的数要及时退出所有DFS递归函数。

#include<iostream>
#include<cstdio>
using namespace std;
typedef unsigned __int64 ll;
bool s;
void dfs(ll x,int n,int k)
{
if(s)return;
if(x%n==0)
{
printf("%I64u\n",x);
s=true;
return ;
}
if(k==19)return;
dfs(x*10,n,k+1);
dfs(x*10+1,n,k+1);
}
int main()
{
int n;
while(cin>>n)
{
if(!n)break;
s=false;
dfs(1,n,0);
}
return 0;
}


Q4:POJ-3087

题意:

给定长度都为C两个字符串,S1,S2,和一个要求的结果字符串S12。先把 S2 的最下面一张牌放在最下面,然后S1,S2交错的叠放,得到新的S12,再把S12最下面的C个字符赋值给S1,把剩下的赋值给S2,再次重复上面的过程。最后求出要得到目标字符串S,问最少需要几步。

思路:

直接BFS模拟,将下一步可得的结果放入队列中(题目所给出的输入是从低向上排列的。)

#include<string>
#include<iostream>
#include<queue>
#include<map>
using namespace std;
int n;
map<string,int>ant;
string answer;
string f(string s1,string s2)
{
string s12;
for(int i=0;i<n;i++)
{
s12+=s2[i];
s12+=s1[i];
}
return s12;
}

int bfs(string s1,string s2)
{
ant.clear();
string s12=f(s1,s2);
ant[s12]=1;
queue<string>q;
q.push(s12);
while(!q.empty())
{
string s=q.front();
s1.clear();
s2.clear();
q.pop();
if(s==answer)return ant[s];
for(int i=0;i<n;i++)s1+=s[i];
for(int i=n;i<2*n;i++)s2+=s[i];
s12=f(s1,s2);
if(ant.find(s12)!=ant.end())return -1;
ant[s12]=ant[s]+1;
q.push(s12);
}
return -1;
}
int main()
{
int k;
cin>>k;
for(int i=1;i<=k;i++)
{
cin>>n;
string s1,s2;
cin>>s1>>s2;
cin>>answer;
cout<<i<<" "<<bfs(s1,s2)<<endl;
}
return 0;
}


Q5:POJ-3414

题意:

给你两个容器 A B 问是否能够经过有限的步骤倒水,得到容量为 C 的水

输出最小的步数,同时输出每一步的操作。

如果不能达到目标状态,则输出 impossible

思路:

总状态只有那么多, 反正每一步后都只有 6 种操作明显的 BFS 关键是每一步路径的记录。

开始用 BFS + 容器做了一遍,发现还是无法处理好输出路径问题,只好重新开始用数组模拟。

容器虽然很好用又方便,但是在不考虑内存的状况下,效率终究比不上数组模拟的了。

注意到 A 和 B 的范围是 1 到 100 的整数,

那么我们可以用vis[i][j]来记录每一种状态 0 <= i, j <= 100 ;

i 表示目前 A 容器中的水, j 表示目前 B 容器中的水

应该很容易就能分析出,对应于每一种状态的下一步只有六种情况:

一:用水池中的水装满 A

二:用水池中的水装满 B

三:把 A 中的水全部倒进废水池

四:把 B 中的水全部倒进废水池

五:把 A 中的水倒进 B 【不能溢出】

那么对应可能会有两种状态:用 k1 表示 A 中的水, k2 表示 B 中的水

如果 k1+k2 <= B 则 k2 = k1+k2; k1 = 0 【不能够装满容器 B】注意顺序

否则 k1 = k1+k2-B; k2 = B 【能够装满容器 B】

六:把 B 中的水倒进 A 【不能溢出】

也有两种情况,分析同上

如果 k1+k2 <= A 则 k1 = k1+k2; k2 = 0;

否则 k2 = k1+k2-A; k1 = A

用结构体数组来模拟队列

用 k1,k2 来记录当前容器中水的状态

前面已经分析过对应于每种情况只有 6 种操作, 那么对应每种情况的操作记录为 1 到 6 输出时处理下就好了。

当然少不了记录到当前状态最少用了多少步数 step

因为要记录路径,所以定义一个 f 来记录上一步在数组模拟队列中的下标。

最后如果能够达到目的,在判定最后一步的时候记录下最后一步在数组中的编号 lastIndex

然后从lastIndex从后往前找【f】前一个步骤在数组中的编号存在 id[] 中

最后再按照扫描出的路径依次遍历即可。

#include<iostream>
#include<cstring>
#include<queue>
#include<string>
using namespace std;

struct node
{
int a,b,f,step;///a为第一个瓶子得水量,b为第二个,f为上一步的下标,step表示步数
string s;
}pot[100000];
int visit[110][110];
int x,y,ans;
string cnt[10000];
void bfs()
{
int head = 0, tail = 1;
pot[0].a = pot[0].b = pot[0].step = 0;
pot[0].f = -1;
visit[0][0] = 1;
while(head != tail)
{
if(pot[head].a == ans || pot[head].b == ans)
{
cout<<pot[head].step<<endl;
int i = -1;
while(pot[head].f != -1)
{
cnt[++i] = pot[head].s;
head = pot[head].f;
}
for(;i >= 0;i--)cout<<cnt[i]<<endl;
return;
}
if(pot[head].a < x)
{
node c;
c.a = x;
c.b = pot[head].b;
c.f = head;
c.step = pot[head].step + 1;
c.s = "FILL(1)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].b < y)
{
node c;
c.a = pot[head].a;
c.b = y;
c.f = head;
c.step = pot[head].step + 1;
c.s = "FILL(2)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].a)
{
node c;
c.a = 0;
c.b = pot[head].b;
c.f = head;
c.step = pot[head].step + 1;
c.s = "DROP(1)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].b)
{
node c;
c.a = pot[head].a;
c.b = 0;
c.f = head;
c.step = pot[head].step + 1;
c.s = "DROP(2)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].a + pot[head].b <= y)
{
node c;
c.a = 0;
c.b = pot[head].a + pot[head].b;
c.f = head;
c.step = pot[head].step + 1;
c.s = "POUR(1,2)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].a + pot[head].b > y)
{
node c;
c.a = pot[head].a + pot[head].b - y;
c.b = y;
c.f = head;
c.step = pot[head].step + 1;
c.s = "POUR(1,2)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].a + pot[head].b <= x)
{
node c;
c.a = pot[head].a + pot[head].b;
c.b = 0;
c.f = head;
c.step = pot[head].step + 1;
c.s = "POUR(2,1)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
if(pot[head].a + pot[head].b > x)
{
node c;
c.a = x;
c.b = pot[head].a + pot[head].b - x;
c.f = head;
c.step = pot[head].step + 1;
c.s = "POUR(2,1)";
if(!visit[c.a][c.b])
{
pot[tail++] = c;
visit[c.a][c.b] = 1;
}
}
head++;
}
cout<<"impossible"<<endl;
}
int main()
{
while(cin >> x >> y >> ans)
{
memset(visit, 0, sizeof(visit));
if(ans > x && ans > y)
{
cout<<"impossible"<<endl;
continue;
}
bfs();
}
}


Q6:UVA-11624

题意:

在一个矩形方阵里面,一个人要从一个位置走向另一个位置,其中某些地方有火源,每过一分钟,火源就会点燃相邻的点,同时相邻的点也变成了火源。人不能通过有火的点。问一个人能够安全地走到目的地去?最短时间多少?

思路:

Joe的起点唯一,但是起火的地方并不是唯一的,这是我们首先要明确的。

关键是之后应该怎样处理火势的蔓延与Joe逃跑这两个过程的关系,我们要清楚:只有火势可以影响Joe的逃跑路线,但Joe的逃跑路线绝对不能影响火势,所以这两个过程不可能出现在同一个BFS中。

可以这样想:既然火势的蔓延时不随人的主观意愿而改变的,那么我们可以先让火势肆意蔓延,看它到底能烧到哪里,以及烧到某个地方所需要的时间,这样,主人公在逃跑的过程中,只要在火势到达之前赶到某个地方就可以了。

综上,需要两个BFS,第一个计算火势蔓延到任意一点所需要的时间,如果火势永远到达不了某些点,就把这些点的时间设为正无穷,之后再搜索Joe的逃跑路线,条件要增加时间这一项,只要Joe能到达迷宫的边界,就算逃出来了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
char Map[1010][1010];
bool v[1010][1010];
int ans[1010][1010];
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
struct Node
{
int x, y, step;
Node(int xx, int yy, int ss):x(xx),y(yy),step(ss){}
};
int n, m, x0, y0;
queue<Node>q;
bool judge(int x, int y)
{
return (x >= 0 && x < n && y >= 0 && y < m && !v[x][y]);
}

void bfs()
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)ans[i][j] = 1 << 30;
}///有可能火到不了一些地方,先把ans设置成无穷大
while(!q.empty())
{
Node a = q.front();
q.pop();
for(int i = 0; i < 4; i++)
{
int x = a.x + dir[i][0];
int y = a.y + dir[i][1];
if(judge(x, y) && Map[x][y] != '#')
{
v[x][y] = 1;
q.push(Node(x, y, a.step + 1));
ans[x][y] = a.step + 1;
}
}
}
memset(v, 0, sizeof(v));
q.push(Node(x0, y0, 1));
v[x0][y0] = 1;
while(!q.empty())
{
Node a = q.front();
q.pop();
if(a.x == 0 || a.x == n - 1 || a.y == 0 || a.y == m - 1)
{
cout<<a.step<<endl;
return ;
}
for(int i = 0; i < 4; i++)
{
int x = a.x + dir[i][0];
int y = a.y + dir[i][1];
if(judge(x, y) && Map[x][y] == '.' && a.step + 1 <= ans[x][y])
{
v[x][y] = 1;
q.push(Node(x, y, a.step + 1));
}
}
}
cout<<"IMPOSSIBLE"<<endl;
}
int main()
{
int T;
cin >> T;
while(T--)
{
while(!q.empty())q.pop();
memset(Map, 0, sizeof(Map));
memset(v, 0, sizeof(v));
cin >> n >> m;
for(int i = 0; i < n; i++)
{
cin >> Map[i];
for(int j = 0; j < m; j++)
{
if(Map[i][j] == 'J')
{
x0 = i;
y0 = j;
}
if(Map[i][j] == 'F')
{
v[i][j] = 1;
q.push(Node(i, j, 0));
}
}
}
bfs();
/*
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
cout<<ans[i][j]<< " ";
cout<<endl;
}*/
}
return 0;
}


Q7:POJ-3984

题意:

中文题

思路:

反正就是简单的结构体套结构体存一下父节点就行,由于记录路径,这里直接用数组代替队列

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

struct node
{
int x,y,f;
}a[30];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int visit[6][6];
int lode[25][2];
int main()
{
int Map[5][5];
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)cin>>Map[i][j];
a[0].x=a[0].y=0;a[0].f=-1;
visit[0][0]=1;
int head=0;int tail=1;
while(head!=tail)
{
if(a[head].x==4&&a[head].y==4)
{
int i=0;
while(a[head].f!=-1)
{
lode[i][0]=a[head].x;
lode[i][1]=a[head].y;
i++;
head=a[head].f;
}
lode[i][0]=lode[i][1]=0;
for(;i>=0;i--)
{
printf("(%d, %d)\n",lode[i][0],lode[i][1]);
}
break;
}
for(int i=0;i<4;i++)
{
int x=a[head].x+dir[i][0];
int y=a[head].y+dir[i][1];
if(x>=0&&x<5&&y>=0&&y<5&&!visit[x][y]&&Map[x][y]==0)
{
a[tail].x=x;
a[tail].y=y;
a[tail].f=head;
visit[x][y]=1;
tail++;
}
}
head++;
}
return 0;
}


Q8:HDU-1241

题意:

就是给你一个地图,找出所有不相连(八个方向)的@组合有多少个

思路:

先对整个地图进行遍历,找到一个入口,然后用DFS深搜,相连的@都找到,并且将其标记为1,再找下一个入口,再DFS能够到达的所有@标记为2,最后的数字就是油田的数量。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
char Map[110][110];
int dir[8][2]={0,1,1,0,0,-1,-1,0,1,1,1,-1,-1,1,-1,-1};
int m,n;
int visit[110][110];
int num;
void dfs(int i,int j)
{
if(visit[i][j])return;
visit[i][j]=num;
for(int k=0;k<8;k++)
{
int x=i+dir[k][0];
int y=j+dir[k][1];
if(x>=0&&x<m&&y>=0&&y<n&&Map[x][y]=='@'&&!visit[x][y])dfs(x,y);
}
}
int main()
{
while(cin>>m>>n)
{
if(!m&&!n)break;
memset(visit,0,sizeof(visit));
memset(Map,0,sizeof(Map));
getchar();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>Map[i][j];
}
getchar();
}
num=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
if(Map[i][j]=='@'&&!visit[i][j])
{
num++;
dfs(i,j);
}
}

cout<<num<<endl;
}return 0;
}


Q9:HDU - 1495

题意:

中文题

思路:

我们有明确的初始状态(s=s,a=0,b=0)和终止状态(s=s>>1,a=s>>1,b=0) (PS:a为大号杯)

而每一步我们都有六个可选择的方向(s->a ;s->b ; a->s ; a->b ;b->s ;b->a),我们要得到最少的倒的次数,当然就是bfs咯。

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int cnt[3],s;
};
int visit[110][110][110];
int b[3],half;
bool f(int x)
{
if(x==half)return true;
else return false;
}
void bfs()
{
queue<node>q;
node a;
a.cnt[0]=b[0];
a.cnt[1]=a.cnt[2]=a.s=0;
visit[a.cnt[0]][a.cnt[1]][a.cnt[2]]=1;
q.push(a);
while(!q.empty())
{
a=q.front();
q.pop();

if(f(a.cnt[0])&&f(a.cnt[1])||f(a.cnt[0])&&f(a.cnt[2])||f(a.cnt[1])&&f(a.cnt[2]))
{
cout<<a.s<<endl;
return ;
}
for(int i=0;i<3;i++)
{
if(a.cnt[i]>0)
{
for(int j=0;j<3;j++)
{
node temp=a;
if(i==j)continue;
if(temp.cnt[i]+temp.cnt[j]>=b[j])
{
temp.cnt[i]+=temp.cnt[j]-b[j];
temp.cnt[j]=b[j];
}
else
{
temp.cnt[j]+=temp.cnt[i];
temp.cnt[i]=0;
}
if(!visit[temp.cnt[0]][temp.cnt[1]][temp.cnt[2]])
{
temp.s++;
q.push(temp);
visit[temp.cnt[0]][temp.cnt[1]][temp.cnt[2]]=1;
}
}
}
}
}
cout<<"NO"<<endl;
}
int main()
{
while(cin>>b[0]>>b[1]>>b[2])
{
memset(visit,0,sizeof(visit));
if(b[0]+b[1]+b[2]==0)break;
if(b[0]%2!=0)
{
cout<<"NO"<<endl;
continue;
}
else
{
half=b[0]/2;
bfs();
}
}
return 0;
}


Q10:HDU - 2612

题意:

Y和M要在KFC约会,地图上有多个KFC,求在哪个KFC两人所走总距离最短,(注意KFC可以当做路)

思路:

用2次BFS,分别求出2个点到各个KFC的最短距离,然后找出和最小的即可

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
char Map[210][210];
int visit[210][210];
int sum1[210][210],sum2[210][210];
int dir[4][2]={1,0,0,1,-1,0,0,-1};
int m,n;
struct node
{
int x,y,s;
node(int xx,int yy,int ss):x(xx),y(yy),s(ss){}
};
int main()
{
while(cin>>m>>n)
{
int x1,y1,x2,y2,p[5];
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(Map,0,sizeof(Map));
getchar();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin>>Map[i][j];
if(Map[i][j]=='Y')
{
p[1]=i;p[2]=j;
}
if(Map[i][j]=='M')
{
p[3]=i;p[4]=j;
}

}  getchar();
}
queue<node>q;
memset(visit,0,sizeof(visit));
q.push(node(p[1],p[2],0));
visit[p[1]][p[2]]=1;
while(!q.empty())
{
node a=q.front();
for(int j=0;j<4;j++)
{
int x=a.x+dir[j][0];
int y=a.y+dir[j][1];
if(x>=0&&x<m&&y>=0&&y<n&&!visit[x][y]&&Map[x][y]!='#')
{
visit[x][y]=1;
q.push(node(x,y,a.s+1));
sum1[x][y]=a.s+1;
}
}
q.pop();
}
memset(visit,0,sizeof(visit));
q.push(node(p[3],p[4],0));
visit[p[3]][p[4]]=1;
while(!q.empty())
{
node a=q.front();
for(int j=0;j<4;j++)
{
int x=a.x+dir[j][0];
int y=a.y+dir[j][1];
if(x>=0&&x<m&&y>=0&&y<n&&!visit[x][y]&&Map[x][y]!='#')
{
visit[x][y]=1;
q.push(node(x,y,a.s+1));
sum2[x][y]=a.s+1;
}
}
q.pop();
}
int minx=1<<30;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(Map[i][j]=='@'&&sum1[i][j]&&sum2[i][j])minx=min(minx,sum1[i][j]+sum2[i][j]);
}
}
cout<<minx*11<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dfs bfs