深度优先搜索&&广度优先搜索 训练赛

目录

A - Oil Deposits   (POJ-1562)DFS

D - Prime Ring Problem  (HDU-1016)DFS

E - A计划  (HDU-2102)BFS

F - 胜利大逃亡  (HDU 1253)BFS

H - A  Strange Lift(HDU 1548)BFS

I - Catch That Cow  (HDU 2717)

深度优先搜索(DFS)基本模板

void dfs (int step)
{
    判断边界
    尝试每一种可能
    for (int i = 1; i<=n; i++)
    {
        继续下一步
        dfs(step+1);
        取消标记
    }
    返回
}

广度(宽度)优先搜索(BFS)基本模板

看下面的BFS题吧,基本都是一个模板。

A - Oil Deposits   (POJ-1562)DFS

 

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid.

Input

The input contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket. 
 

Output

are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets.

Sample Input

1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5 
****@
*@@*@
*@**@
@@@*@
@@**@
0 0

Sample Output

0
1
2
2

题意

有个地方发现了石油,地质学家想知道这块地方有几个油田。输入m,n,代表这块地方的大小,然后输入这块地方的石油分布,“ * ”代表陆地,“ @ ”代表石油,相邻或对角相邻的两个@代表同一块油田,要求根据输入的地图输出这块地方的油田个数。

思路

这道题其实就是求图中独立子图的个数,类似于求海中海岛个数的题,不同之处在于这个油田对角相邻也算同一块。我的想法是从左上角第一个点依次扫过去,每次碰到@都用dfs把整个油田扫出来,并把扫过的@全都变为*,定义一个计数器记录油田的个数。

AC代码

#include <iostream>
#include<stdio.h>
#include <algorithm>
using namespace std;


char map[101][101];
int n, m, p;
void dfs(int i, int j)//深度优先搜索
{
    if(map[i][j]!='@' || i<0 || j<0 || i>=m || j>=n) return;
    else
    {
        map[i][j]='*';//扫过的都变成'*'
        dfs(i-1, j-1);//枚举所有相邻位置
        dfs(i-1, j);
        dfs(i-1, j+1);
        dfs(i, j-1);
        dfs(i, j+1);
        dfs(i+1, j-1);
        dfs(i+1, j);
        dfs(i+1, j+1);
    }
}
int main()
{
    int i, j;
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        if(m==0 || n==0) break;
        p = 0;
        for(i = 0; i < m; i++)
            for(j = 0; j < n; j++)
                cin>>map[i][j];
        for(i = 0; i < m; i++)
        {
            for(j = 0; j < n; j++)
            {
                if(map[i][j] == '@')
                {
                    dfs(i, j);
                    p++;           //计数器,记录油田的个数
                }
            }
        }
        printf("%d\n",p);
    }
 
    return 0;
}

 

D - Prime Ring Problem  (HDU-1016)DFS

 

A ring is compose of n circles as shown in diagram. Put natural number 1, 2, ..., n into each circle separately, and the sum of numbers in two adjacent circles should be a prime. 

Note: the number of first circle should always be 1. 

Input

n (0 < n < 20). 

Output

The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order. 

You are to write a program that completes above process. 

Print a blank line after each case. 

Sample Input

6
8

Sample Output

Case 1:
1 4 3 2 5 6
1 6 5 2 3 4

Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2

 

题意

输入一个数n,要求输出从1到n满足任意相邻两项之和(包括第1项和第n项之和)为素数并且第1项是1的所有排列。

思路

由于n<20因此可以直接列出38以内所有的素数,每次通过查询素数表来确定相邻两个数的和是否是素数,然后就可以愉快地写dfs函数了,用dfs函数递归出所有结果即可。这题还有需要的一点是格式问题,我就是没注意格式错了一次,输出排列时每两个数之间有一个空格,最后一个数后没有空格,每两个Case之间有一个空行。

AC代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>

using namespace std;

int a[20],vis[21];
int n;
int primelist[38] = {0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1};  //素数表

void dfs (int cur){
	if (cur == n && primelist[a[0]+a[n-1]]){
		cout << a[0];
		for (int i = 1; i<n; i++){
			cout << " " << a[i];  //最后一个数后不能有空格
		}
		cout << endl;
	}
	else{
		for (int i = 2; i<=n; i++){
			if (!vis[i] && primelist[i+a[cur-1]]){
				a[cur] = i;
				vis[i] = 1;
				dfs(cur+1);
				vis[i] = 0;   //注意递归结束后应该把标记取消,不然只能输出一种结果了
			}
		}
	}
}

int main()
{
	a[0] = 1;
	int num = 1;
	while (~scanf("%d",&n)){
		memset(vis,0,sizeof(vis));
		printf("Case %d:\n",num);
		num++;
		dfs(1);
		printf("\n");  //注意两个n的答案之间应该有一个空行,刚开始没注意这个错了一次
	}
	return 0;
}

 

E - A计划  (HDU-2102)BFS

可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸的她再一次面临生命的考验。魔王已经发出消息说将在T时刻吃掉公主,因为他听信谣言说吃公主的肉也能长生不老。年迈的国王正是心急如焚,告招天下勇士来拯救公主。不过公主早已习以为常,她深信智勇的骑士LJ肯定能将她救出。 
现据密探所报,公主被关在一个两层的迷宫里,迷宫的入口是S(0,0,0),公主的位置用P表示,时空传输机用#表示,墙用*表示,平地用.表示。骑士们一进入时空传输机就会被转到另一层的相对位置,但如果被转到的位置是墙的话,那骑士们就会被撞死。骑士们在一层中只能前后左右移动,每移动一格花1时刻。层间的移动只能通过时空传输机,且不需要任何时间。

Input

输入的第一行C表示共有C个测试数据,每个测试数据的前一行有三个整数N,M,T。 N,M迷宫的大小N*M(1 <= N,M <=10)。T如上所意。接下去的前N*M表示迷宫的第一层的布置情况,后N*M表示迷宫第二层的布置情况。

Output

如果骑士们能够在T时刻能找到公主就输出“YES”,否则输出“NO”。

Sample Input

1
5 5 14
S*#*.
.#...
.....
****.
...#.

..*.P
#.*..
***..
...*.
*.#..

Sample Output

YES

 题意

公主被魔王绑架关在迷宫里,迷宫有两层,“.”是平地“*”是墙“#”是时空机可以穿梭到另外一层迷宫的对应位置,如果穿梭过去的对应位置是墙壁则骑士会被撞死,如果穿梭过去还是#则骑士就被困在那里了,穿梭不需要时间,骑士每走一格会花费1时间。魔王会在T时刻吃掉公主,要求我们根据输入的时间T和地图来判断骑士能否救出公主,能就输出YES,否则输出NO.

思路

这题用bfs扩展每一个点,由于迷宫分两层所以要用一个三维数组数组map来存地图,要注意的是骑士走到#会穿到另一层,题目中只说了如果传过去的对应位置是墙壁则骑士会撞死,还有一种情况是传过去的位置还是#,这样骑士也是无法走出去的。注意到这些细节后按照bfs模板按照题目要求一个个点扩展就可以了。

AC代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>

using namespace std;

int N,M,T;
char map[2][11][11];
//int a[2][11][11];
int book[2][11][11];
int head[3],tail[3];
int go[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};

struct node 
{
	int x;
	int y;
	int floor;
	int step;
};

int check (int floor,int x,int y){
	if (x<0 || y<0 || x>=N || y>=M){    //检查是否在图内 
		return 1;
	}
	if (map[floor][x][y] == '*'){      //检查点是否是墙 
		return 1;
	}
	return 0;
}

void bfs (){
	memset(book,0,sizeof(book));
	node a,next;
	queue<node> Q;
	a.floor = head[0];
	a.x = head[1];
	a.y = head[2];
	a.step = 0;
	book[head[0]][head[1]][head[2]] = 1;
	Q.push(a);
	while (!Q.empty()){
		a = Q.front();
		Q.pop();
		if (a.floor == tail[0] && a.x == tail[1] && a.y == tail[2]){
			printf("YES\n");
			return ;
		}
		if (a.step >= T){
			break;
		}
		for (int i = 0; i<4; i++){
			next = a;
			next.x += go[i][0];
			next.y += go[i][1];
			next.step++;
			if (check(next.floor,next.x,next.y)){         //检查扩展后的点是否在边界内而且不是墙 
				continue;
			}
			if (book[next.floor][next.x][next.y]){        //检查有没有重复扩展相同的点  
				continue;
			}
			book[next.floor][next.x][next.y] = 1;        //将扩展后的点标记为1,表示已走过
			if (map[next.floor][next.x][next.y] == '#'){
				next.floor = !next.floor;
				if (check(next.floor,next.x,next.y))
					continue;
				if (book[next.floor][next.x][next.y])
					continue;
				if (map[next.floor][next.x][next.y] == '*' || map[next.floor][next.x][next.y] == '#')
					continue;
				book[next.floor][next.x][next.y] = 1;	 
			}
			if (next.floor == tail[0] && next.x == tail[1] && next.y == tail[2]){
				printf("YES\n");
				return ;
			} 
			Q.push(next);
		}
	}
	printf("NO\n");
}


int main()
{
   int C;
   scanf("%d",&C);
   while (C--){
   		scanf("%d%d%d",&N,&M,&T);
   		for (int k = 0; k<2; k++){
   			for (int i = 0; i<N; i++){
   				scanf("%s",map[k][i]);
   				for (int j = 0; j<M; j++){
   					if (map[k][i][j] == 'S'){
   						head[0] = k;
   						head[1] = i;
   						head[2] = j;
					   }
					   else if (map[k][i][j] == 'P'){
					   	tail[0] = k;
					   	tail[1] = i;
					   	tail[2] = j;
					   }
				   }
			   }
		   }
		   bfs();
   }
 
    return 0;
}

 

F - 胜利大逃亡  (HDU 1253)BFS

Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会. 

魔王住在一个城堡里,城堡是一个A*B*C的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,Ignatius每分钟能从一个坐标走到相邻的六个坐标中的其中一个.现在给你城堡的地图,请你计算出Ignatius能否在魔王回来前离开城堡(只要走到出口就算离开城堡,如果走到出口的时候魔王刚好回来也算逃亡成功),如果可以请输出需要多少分钟才能离开,如果不能则输出-1. 

 

Input

输入数据的第一行是一个正整数K,表明测试数据的数量.每组测试数据的第一行是四个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0块,然后是第1块,第2块......),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙.(如果对输入描述不清楚,可以参考Sample Input中的迷宫描述,它表示的就是上图中的迷宫) 

特别注意:本题的测试数据非常大,请使用scanf输入,我不能保证使用cin能不超时.在本OJ上请使用Visual C++提交. 

Output

对于每组测试数据,如果Ignatius能够在魔王回来前离开城堡,那么请输出他最少需要多少分钟,否则输出-1. 

Sample Input

1
3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0

Sample Output

11

题意

这题和E题很类似,只不过把两层的迷宫变成了一个立体的图形,也是要判断能否在T时间内走出迷宫,能走出的话输出最少时间,否组输出-1.

思路

思路和上题也类似,用bfs扩展每一个点,找出最短路,然后判断时间是否符合要求,最后输出即可。要注意的是本题数据量较大如果用cin输入可能会超时,应该用scanf输入更加好。还要注意特判的情况,如果a+b+c-3>t或mp[a-1][b-1][c-1]==1就一定出不去,如果a==b==c==1,一定能出去且时间为0。

AC代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
 
int a,b,c,t;
int mp[55][55][55];
struct node
{
	int x,y,z;
	int step;
};
 
int dir[6][3]={1,0,0,-1,0,0,0,0,1, 0,0,-1, 0,1,0, 0,-1,0};
 
int  bfs()
{
    queue<node> Q;
	node begin={0,0,0,0},p;
    mp[0][0][0] = 1;
    Q.push(begin);
    while(!Q.empty())
	{
        p=Q.front() ;
        Q.pop() ;
        for(int i=0; i<6; i++)
		{
            node temp = p ;
            temp.x += dir[i][0] ;
            temp.y += dir[i][1] ;
            temp.z += dir[i][2] ;
            if(temp.x==a-1&&temp.y==b-1&&temp.z==c-1&&temp.step<=t)
			{
				return temp.step+1;
            }
 
            if(temp.x>=0&&temp.x<a&&temp.y>=0&&temp.y<b&&temp.z>=0&&temp.z<c&&!mp[temp.x][temp.y][temp.z])
			{
                mp[temp.x][temp.y][temp.z] = 1 ;
                temp.step++ ;
                Q.push(temp) ;
            }
        }
    }
    return -1;
}
int main()
{
	int num,i,j,k;
	scanf("%d",&num);
	while(num--)
	{
		memset(mp,-1,sizeof(mp));
		scanf("%d%d%d%d",&a,&b,&c,&t);
		for(i=0;i<a;i++)
			for(j=0;j<b;j++)
				for(k=0;k<c;k++)
					scanf("%d",&mp[i][j][k]);
		mp[0][0][0]=0;
		if(a+b+c-3>t||mp[a-1][b-1][c-1]==1)
		{
			printf("-1\n");
			continue;
		}
		if(a==b==c==1)
		{
			printf("0\n");
			continue;
		}
		printf("%d\n",bfs());
	}
	return 0;
}

 

H - A  Strange Lift(HDU 1548)BFS

 

There is a strange lift.The lift can stop can at every floor as you want, and there is a number Ki(0 <= Ki <= N) on every floor.The lift have just two buttons: up and down.When you at floor i,if you press the button "UP" , you will go up Ki floor,i.e,you will go to the i+Ki th floor,as the same, if you press the button "DOWN" , you will go down Ki floor,i.e,you will go to the i-Ki th floor. Of course, the lift can't go up high than N,and can't go down lower than 1. For example, there is a buliding with 5 floors, and k1 = 3, k2 = 3,k3 = 1,k4 = 2, k5 = 5.Begining from the 1 st floor,you can press the button "UP", and you'll go up to the 4 th floor,and if you press the button "DOWN", the lift can't do it, because it can't go down to the -2 th floor,as you know ,the -2 th floor isn't exist. 
Here comes the problem: when you are on floor A,and you want to go to floor B,how many times at least he has to press the button "UP" or "DOWN"? 

Input

The input consists of several test cases.,Each test case contains two lines. 
The first line contains three integers N ,A,B( 1 <= N,A,B <= 200) which describe above,The second line consist N integers k1,k2,....kn. 
A single 0 indicate the end of the input.

Output

For each case of the input output a interger, the least times you have to press the button when you on floor A,and you want to go to floor B.If you can't reach floor B,printf "-1".

Sample Input

5 1 5
3 3 1 2 5
0

Sample Output

3

题意

有一个电梯与众不同,它只有两个按钮:上和下。每一层都对应着一个ki值,这个值表示当电梯在这层时按上或下会上升或下降ki层,最低到达1层,最高到n层。题目要求从输入A层到B层最少需要按几下按钮,如果无法到达就输出-1.

思路

还是用bfs,先定义一个结构体(表示层数和步数)和结构体队列,然后就是用熟悉的bfs模板扩展所有路径,输出最小的即可。简单的模板题,具体看代码吧。

AC代码

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
 
int n,s,e;
int ss[205],vis[205];
 
struct node
{
    int x,step;
};
 
int check(int x)
{
    if(x<=0 || x>n)
    return 1;
    return 0;
}
 
int BFS()
{
    queue<node> Q;
    node a,next;
    int i;
    a.x = s;
    a.step = 0;
    vis[s] = 1;
    Q.push(a);
    while(!Q.empty())
    {
        a = Q.front();
        Q.pop();
        if(a.x == e)
        return a.step;
        for(i = -1;i<=1;i+=2)
        {
            next = a;
            next.x +=i*ss[next.x];
            if(check(next.x) || vis[next.x])
            continue;
            vis[next.x] = 1;
            next.step++;
            Q.push(next);
        }
    }
    return -1;
}
 
int main()
{
    int i,j;
    while(~scanf("%d",&n),n)
    {
        scanf("%d%d",&s,&e);
        for(i = 1;i<=n;i++)
        scanf("%d",&ss[i]);
        memset(vis,0,sizeof(vis));
        printf("%d\n",BFS());
    }
 
    return 0;
}

 

I - Catch That Cow  (HDU 2717)

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting. 

* Walking: FJ can move from any point X to the points X - 1 or X + 1 in a single minute 
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute. 

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?

Input

Line 1: Two space-separated integers: N and K

Output

Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.

Sample Input

5 17

Sample Output

4

Hint

The fastest way for Farmer John to reach the fugitive cow is to move along the following path: 5-10-9-18-17, which takes 4 minutes.
        
 

题意

农夫要抓一头牛,那头牛在固定的位置K不动,农夫在位置N开始,N和K的差就是他们之间的距离,农夫有两种移动方式,第一种是步行,每分钟可以移动到左右相邻的位置,即N+1或N-1;第二种是运输,每分钟可以移动到当前位置两倍的地方,如N移动到2N。现在要求农夫从N到牛的位置K最少要几分钟。

思路

这题和H题类似,都是直线上的最小步数问题,还是用bfs搜索所有走法,然后输出最小的步数即可。有个地方需要注意:N的范围不是农夫的移动范围,如K = 100000时,N可以先到100002,再往左走两步到K,因此vis数组要开的大一点。

AC代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>

using namespace std;

int n,k;
int time = 0;
int vis[100005];
int min = 99999;
struct point {
	int x,step;
} st;
queue <point> q;

int bfs(){
	while (!q.empty()){
		q.pop();
	}
	memset(vis,0,sizeof(vis));
	vis[st.x] = 1;        //开始位置标记为1 
	q.push(st);
	while (!q.empty()){
		point now = q.front();
		if (now.x == k){
			return now.step;
		}
		q.pop();
		for (int i = 0; i<3; i++){
			point next = now;
			if (i == 0){
				next.x += 1;
			}
			else if (i == 1){
				next.x -= 1;
			}
			else if (i == 2){
				next.x *= 2;
			}
			next.step++;
			if (next.x == k){
				return next.step;
			}
			if (next.x >0 && next.x <=100005 && !vis[next.x]){
				vis[next.x] = 1;
				q.push(next);
			}
		}
	}
	return 0; 
}

int main()
{
	while (~scanf("%d%d",&n,&k)){
		st.x = n;
		st.step = 0;
		printf("%d\n",bfs());
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值