深度优先搜索(dfs)和广度优先搜索(bfs)专题题解

Oil Deposits

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

题意

寻找连通起来的@。上下左右斜对角的也算。

思路

经典DFS。

#include<cstdio>
#include<cstring>
const int maxn = 100+5;

char pic[maxn][maxn];
int m,n,idx[maxn][maxn];

void dfs(int r,int c,int id)
{
	if(r<0||r>=m||c<0||c>=n)
	{
		return ;//出界的格子 
	}
	if(idx[r][c]>0||pic[r][c]!='@') 
	{
		return ;//不是@ 或者已经访问过的格子 
	}
	idx[r][c]=id;
	for(int dr=-1;dr<=1;dr++)
	for(int dc=-1;dc<=1;dc++)
	if(dr!=0||dc!=0)
	dfs(r+dr,c+dc,id);
}

int main()
{
	while(scanf("%d%d",&m,&n)==2&&m&&n)
	{
		for(int i=0;i<m;i++)
		scanf("%s",pic[i]);
		
		memset(idx,0,sizeof(idx));
		int cnt=0;
		for(int i=0;i<m;i++)
		for(int j=0;j<n;j++)
		if(idx[i][j]==0&&pic[i][j]=='@')
		dfs(i,j,++cnt);
		
		printf("%d\n",cnt);
		
	}
	return 0;
}

Prime Ring Problem

Problem Description

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

题意

从1~n排列组合,找出满足前后两个数字相加全为素数的顺序,1为头,同时最后一个与1相加也为素数。

思路

深度优先搜索的题目,暴力会超时,要用dfs回溯法。

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n;
int a[20],vis[20];

int isp(int n)           //判断是否为素数
{
    if(n<2)
        return false;
    for (int i=2;i*i<=n; i++)
    {
        if(n % i == 0)
            return false;
    }
    return true;
}

void dfs(int s)
{
    if(s==n&&isp(a[1]+a[n]))  //递归边界。别忘了测试第一个数和最后一个数
    {
        for(int i=1; i<n; i++)
            cout<<a[i]<<" ";
        cout<<a[n]<<endl;
    }
    else
    {
        for(int i=2; i<=n; i++)
        {
            if(!vis[i]&&isp(i+a[s]))   //如果i没有用过,并且与前一个数之和为素数
            {
                a[s+1]=i;
                vis[i]=1;              //标记
                dfs(s+1);
                vis[i]=0;              //清除标记
            }
        }
    }
}
int main()
{
    int t=0;
    while(cin>>n)
    {
        memset(vis,0,sizeof(vis));
        a[1]=1;
        //if(t!=0) cout<<endl;            //一定注意输出格式
        t++;
        cout<<"Case "<<t<<":"<<endl;
        dfs(1);
        cout<<endl;
    }
    return 0;
}

A计划
Problem Description

可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸的她再一次面临生命的考验。魔王已经发出消息说将在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

思路

广度优先搜索的题。遇到传送门一定要传送,但要小心传送过去是墙,或者是两个传送阵互传。

​
#include<stdio.h>
#include <string.h>
#include <queue>
#include<iostream>  
using namespace std;
int n,m,T,dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};

char a[2][12][12];

struct point{  
    int x,y,step,type;  
}   P[105];
  
int bfs(point X)
{
	queue<point> Q;
	point t,tt;
	Q.push(X);
	while(!Q.empty())
	{
		t=Q.front();
		Q.pop();
		if(a[t.type][t.x][t.y]=='*')
		continue;
		if(a[t.type][t.x][t.y]=='P')
		return 1;
		
		a[t.type][t.x][t.y]='*';//将走过的点变为墙 
		
		for(int i=0;i<4;i++)
		{
			tt.x=t.x+dir[i][0];
			tt.y=t.y+dir[i][1];
			tt.step=t.step+1;
			tt.type=t.type;
			
			if(tt.step>T||a[tt.type][tt.x][tt.y]=='*')
			continue;
			
			if(a[tt.type][tt.x][tt.y]=='#')
			{
				a[tt.type][tt.x][tt.y]=='*';
				tt.type=1-tt.type;
				if(a[tt.type][tt.x][tt.y]=='#'||a[tt.type][tt.x][tt.y]=='*')
				{
					a[tt.type][tt.x][tt.y]=a[1-tt.type][tt.x][tt.y]='*';
					continue;
				}
			}
			Q.push(tt);
		}
	}
	return 0;

}

int main()
{
	int c;
	point S;
	int i,j,k;
	scanf("%d",&c);
	while(c--)
	{
		memset(a,'*',sizeof(a));
		scanf("%d%d%d",&n,&m,&T);
	    for(i=1;i<=n;i++)    for(j=1;j<=m;j++)    cin>>a[0][i][j];  
        for(i=1;i<=n;i++)    for(j=1;j<=m;j++)    cin>>a[1][i][j];  
		
		S.x=1;S.y=1;S.step=0;S.type=0;  
		printf("0\n");
		if(bfs(S)) 
		printf("YES\n");
		else
		printf("NO\n");
	}
	return 0;
}

​

 

胜利大逃亡

Problem Description

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

思路

这道题目的解法和A计划差不多,只不过要进行的方向由4个变为了6个。

我这道题目wrong了几次,最后发现有一个坑,就是起点是可以为墙的。。。

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100;
int n,m,T,z;
int a[maxn][maxn][maxn];
int dir[6][3]={{0,0,1},{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}};
struct point{  
    int x,y,step,type;  
}   P[1005];

int bfs(point X)
{
	point t,tt;
	queue<point> Q;  
	Q.push(X);
	while(!Q.empty())
	{
		
		t=Q.front();
		Q.pop();  
		if(z-t.type<(n-1-t.x)+(m-1-t.y)+(T-1-t.type))
		continue;
		if(t.x!=0&&t.y!=0&&t.type!=0){
		
		if(a[t.x][t.y][t.type]==1||t.step>=z)
		continue;}
		if(t.x==n-1&&t.y==m-1&&t.type==T-1)
		{
			return(t.step);
		}
		
		a[t.x][t.y][t.type]=1;
		
		for(int i=0;i<6;i++)
		{
			tt.x=t.x+dir[i][0];
			tt.y=t.y+dir[i][1];
			tt.type=t.type+dir[i][2];
			tt.step=t.step+1;
			
			if(tt.x<0||tt.x>=n||tt.y<0||tt.y>=m||tt.type<0||tt.type>=T)
			continue;
			
			if(a[tt.x][tt.y][tt.type]==1)
			continue;
			
			Q.push(tt);
			
		}
	}
	return -1;
}
int main()
{
	int C;
	scanf("%d",&C);
	while(C--)
	{
		memset(a,0,sizeof(a));
		scanf("%d%d%d%d",&n,&m,&T,&z);
		int i,j,k;
		for(i=0;i<n;i++)
		for(j=0;j<m;j++)
		for(k=0;k<T;k++)
		scanf("%d",&a[i][j][k]);
		point S;
		S.x=0;
		S.y=0;
		S.type=0;
		S.step=0;
		
		printf("%d\n",bfs(S));
	}
	return 0;
}

A strange lift

Problem Description

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

题意

乘坐一个有点奇怪的电梯,每层楼能上升下降的层数是固定的,给予起点和终点,计算途中需要多少次才能到达终点。

思路

这道其实比之前两道要来的简单,因为只有上下两个变量,要么上,要么下,注意当楼层低于1或者高于N的时候是不能记录的。

#include<cstdio>
#include <string.h>
#include <queue>

using namespace std;

int k[205];
int n,a,b;
int num[2005];
int t,tt,ttt,c;
int bfs(int x)
{
	queue<int> Q;
	Q.push(x);
	t=0;
	tt=0;
	ttt=0;
	while(!Q.empty())
	{
	t=Q.front();
	Q.pop() ;
	if(t==b)
	{
		return num[b];
	}
	
	
	tt=t+k[t];
	if(tt>0&&tt<=n&&num[tt]==0)
	{
	Q.push(tt);
	num[tt]=num[t]+1;
	}
	ttt=t-k[t];
	if(ttt>0&&ttt<=n&&num[ttt]==0)
	{
		Q.push(ttt);
	    num[ttt]=num[t]+1;
	}
	
    }
    
    return -1;
}
int main()
{
	int i;
	while(scanf("%d",&n))
	{
		memset(k,0,sizeof(k));
		memset(num,0,sizeof(num));
		if(n==0)
		break;
		scanf("%d%d",&a,&b);
		for(i=1;i<=n;i++)
		{
			scanf("%d",&k[i]);
		}
		printf("%d\n",bfs(a));
	}
	return 0;
} 

Catch That Cow

Problem Description

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

题意

农夫追捕逃跑的牛,农夫能够往前一格或者往后一格,或者跃迁(???)一倍的格数,奶牛不知道农夫在追,所以不会移动。

思路

和电梯那道思路类似(bfs的题目思路好像都一样。。。),他的条件跟多了一个,翻倍。所以要注意的是数组越界的判断。

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

using namespace std;

int n,m,ans;
int num[200005];
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		queue<int> Q;
		Q.push(n);
		memset(num, 0, sizeof(num) );
		num[n]=1;
		while(!Q.empty())
		{
			int c=Q.front();
			if(c==m)
			{
				ans=num[c];
				break;
			}
			Q.pop();
			if(!num[c-1]&&c-1>0)
			{
				num[c-1]=num[c]+1;
				Q.push(c-1);
			}
			if(!num[c+1]&&c+1<=200001)
			{
				num[c+1]=num[c]+1;
				Q.push(c+1);
			}
			if(c*2<=200000&&!num[c*2])
			{
				num[c*2]=num[c]+1;
				Q.push(2*c);
			}
		}
		printf("%d\n",ans-1);
	}
	return 0;
}

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值