习题课-广搜

武士风度的牛

题目描述

农民 John 有很多牛,他想交易其中一头被 Don 称为 The Knight 的牛。这头牛有一个独一无二的超能力,在农场里像 Knight 一样地跳(就是我们熟悉的象棋中马的走法)。虽然这头神奇的牛不能跳到树上和石头上,但是它可以在牧场上随意跳,我们把牧场用一个 x,y 的坐标图来表示。这头神奇的牛像其它牛一样喜欢吃草,给你一张地图,上面标注了 The Knight 的开始位置,树、灌木、石头以及其它障碍的位置,除此之外还有一捆草。现在你的任务是,确定 The Knight 要想吃到草,至少需要跳多少次。The Knight 的位置用 K 来标记,障碍的位置用 * 来标记,草的位置用 H 来标记。

这里有一个地图的例子:

11 | . . . . . . . . . .
10 | . . . . * . . . . .
9   | . . . . . . . . . .
8   | . . . * . * . . . .
7   | . . . . . . . * . .
6   | . . * . . * . . . H
5   | * . . . . . . . . .
4   | . . . * . . . * . .
3   | . K . . . . . . . .
2   | . . . * . . . . . *
1   | . . * . . . . * . .
0   ----------------------
                              1
0 1 2 3 4 5 6 7 8 9 0

The Knight 可以按照下图中的 A,B,C,D… 这条路径用 5 次跳到草的地方(有可能其它路线的长度也是 5):

11 | . . . . . . . . . .
10 | . . . . * . . . . .
9   | . . . . . . . . . .
8   | . . . * . * . . . .
7   | . . . . . . . * . .
6   | . . * . . * . . . F<
5   | * . B . . . . . . .
4   | . . . * C . . * E .
3   | .>A . . . . D . . .
2   | . . . * . . . . . *
1   | . . * . . . . * . .
0   ----------------------
                              1
0 1 2 3 4 5 6 7 8 9 0

输入描述

第一行: 两个数,表示农场的列数(< =150)和行数(< =150) 第二行..结尾: 如题目描述的图。

输出描述

一个数,表示跳跃的最小次数。

样例输入
10 11
..........
....*.....
..........
...*.*....
.......*..
..*..*...H
*.........
...*...*..
.K........
...*.....*
..*....*..
样例输出
5
提示

Hint:这类问题可以用一个简单的先进先出表(队列)来解决。

代码
#include<iostream>
#include<queue>
using namespace std;
int n,m,c,r,cc,rr;
char a[155][155];
int vis[155][155],cnt,step[155][155];
int dx[8]={-2,-2,-1,-1,1,1,2,2};
int dy[8]={-1,1,-2,2,-2,2,-1,1};
queue<int> qx,qy;
void bfs(){
	qx.push(c);
	qy.push(r);
	vis[c][r]=1;
	while(qx.empty()==0){
		int x=qx.front();
		int y=qy.front();
		qx.pop();
		qy.pop();
		if(x==cc&&y==rr){
			cout<<step[x][y];
			return ;
		}
		for(int i=0;i<8;i++){
			int nx=x+dx[i];
			int ny=y+dy[i];
			if(nx<=n&&nx>=1&&ny<=m&&ny>=1&&vis[nx][ny]==0&&a[nx][ny]!='*'){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=1;
				step[nx][ny]=step[x][y]+1;
			} 
		}
	}
}
int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
			if(a[i][j]=='K'){
				c=i;
				r=j;
			}
			if(a[i][j]=='H'){
				cc=i;
				rr=j;
			}
		}
	}
	bfs();
	return 0;
}

仙岛求药

题目描述

少年李逍遥的婶婶病了,王小虎介绍他去一趟仙灵岛,向仙女姐姐要仙丹救婶婶。叛逆但孝顺的李逍遥闯进了仙灵岛,克服了千险万难来到岛的中心,发现仙药摆在了迷阵的深处。迷阵由M×N个方格组成,有的方格内有可以瞬秒李逍遥的怪物,而有的方格内则是安全。现在李逍遥想尽快找到仙药,显然他应避开有怪物的方格,并经过最少的方格,而且那里会有神秘人物等待着他。现在要求你来帮助他实现这个目标。 

下图 显示了一个迷阵的样例及李逍遥找到仙药的路线。

输入描述

输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于20。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义: 

1)‘@’:少年李逍遥所在的位置; 

2)‘.’:可以安全通行的方格; 

3)‘#’:有怪物的方格; 

4)‘*’:仙药所在位置。 当在一行中读入的是两个零时,表示输入结束。

输出描述

对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。

样例输入
8 8
.@##...#
#....#.#
#.#.##..
..#.###.
#.#...#.
..###.#.
...#.*..
.#...###
6 5
.*.#.
.#...
..##.
.....
.#...
....@
9 6
.#..#.
.#.*.#
.####.
..#...
..#...
..#...
..#...
#.@.##
.#..#.
0 0
样例输出
10
8
-1
代码
#include<iostream>
#include<queue>
#include<string>
#include<cstring>
using namespace std;
int n,m,c,r,cc,rr;
char a[125][125];
int vis[125][125],cnt,step[125][125];
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int f;
void bfs(){
    queue<int> qx,qy;
    f=0;
	qx.push(c);
	qy.push(r);
	vis[c][r]=1;
	while(qx.empty()==0){
		int x=qx.front();
		int y=qy.front();
		qx.pop();
		qy.pop();
		if(x==cc&&y==rr){
		    f=1;
			cout<<step[x][y]<<endl;
			break;
		}
		for(int i=0;i<4;i++){
			int nx=x+dx[i];
			int ny=y+dy[i];
			if(nx<=n&&nx>=1&&ny<=m&&ny>=1&&vis[nx][ny]==0&&a[nx][ny]!='#'){
				qx.push(nx);
				qy.push(ny);
				vis[nx][ny]=1;
				step[nx][ny]=step[x][y]+1;
			} 
		}
	}
}
int main(){
	while(cin>>n>>m){
		if(n==0&&m==0){
			break;
		}
		f=0;
		memset(vis,0,sizeof vis);
		memset(step,0,sizeof step);
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>a[i][j];
				if(a[i][j]=='@'){
					c=i;
					r=j;
				}
				if(a[i][j]=='*'){
					cc=i;
					rr=j;
				}
			}
		}
		bfs();
		if(f==0){
		    cout<<-1<<endl;
		}
	}
	return 0;
}

抓住那头牛

题目描述

农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0≤N≤100000),牛位于点K(0≤K≤100000)。农夫有两种移动方式: 

1、从X移动到X-1或X+1,每次移动花费一分钟 

2、从X移动到2*X,每次移动花费一分钟 

假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?

输入描述

两个整数,N和K。

输出描述

一个整数,农夫抓到牛所要花费的最小分钟数。

样例输入
5 17
样例输出
4
代码
#include<iostream>
#include<queue>
#include<string>
using namespace std;
int n,m;
int vis[100005],cnt,step[100005];
void bfs(){
	queue<int> q;
	q.push(n);
	vis[n]=1;
	while(q.empty()==0){
		int x=q.front();
		q.pop();
		if(x==m){
			cout<<step[x]<<endl;
			return ;
		}
		if((x-1)>=0&&(x-1)<=100000&&vis[x-1]==0){
			q.push(x-1);
			vis[x-1]=1;
			step[x-1]=step[x]+1;
		}
		if((x+1)>=0&&(x+1)<=100000&&vis[x+1]==0){
			q.push(x+1);
			vis[x+1]=1;
			step[x+1]=step[x]+1;
		}
		if((x*2)>=0&&(x*2)<=100000&&vis[x*2]==0){
			q.push(x*2);
			vis[x*2]=1;
			step[x*2]=step[x]+1;
		}
	}
}
int main(){
	cin>>n>>m;
	bfs();
	return 0;
}

The Castle

输入描述

平面图用一个数字表示一个方块(第1个房间用二进制1011表示,0表示无东墙,用十进制11表示)。 

第一行一个整数m(m≤50),表示房子南北方向的长度。 

第二行一个整数n(n≤50),表示房子东西方向的长度。

后面的m行,每行有n个整数,每个整数都表示平面图对应位置的方块的特征。每个方块中墙的特征由数字P来描述(0≤P≤15)。数字P是下面的可能取的数字之和: 

1(西墙 west) 

2(北墙 north) 

4(东墙 east) 

8(南墙 south) 

室内的墙被定义两次: 例如方块(1,1)中的南墙也被位于其南面的方块(2,1)定义了一次。 

建筑中至少有两个房间。

输出描述

第1行:一个整数,表示房间总数; 

第2行:一个整数,表示最大房间的面积(方块数)。

样例
输入
4
7
11 6 11  6  3 10  6
7  9  6 13  5 15  5
1 10 12  7 13  7  5
13 11 10 8 10 12 13
输出
5
9
 代码
#include<iostream>
#include<queue>
#include<string>
using namespace std;
int n,m,maxx,sum;
struct node{
	int f[4];
}a[55][55];
int vis[55][55],cnt;
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
void dfs(int x,int y){
	vis[x][y]=1;
	sum++;
	for(int i=0;i<4;i++){
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(a[x][y].f[i]==0&&nx>=1&&nx<=n&&ny>=1&&ny<=m&&vis[nx][ny]==0){
			dfs(nx,ny);
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			int t;
			cin>>t;
			for(int k=0;k<4;k++){
				a[i][j].f[k]=t%2;
				t=t/2;
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(vis[i][j]==0){
				cnt++;
				sum=0;
				dfs(i,j);
				if(maxx<sum){
					maxx=sum;
				}
			}
		}
	}
	cout<<cnt<<endl<<maxx;
	return 0;
}

Dungeon Master

题目描述

这题是一个三维的迷宫题目,其中用‘.’表示空地,‘W’表示障碍物,‘S’表示起点,‘E’表示终点,求从起点到终点的最小移动次数,解法和二维的类似,只是在行动时除了东南西北移动外还多了上下。可以上下左右前后移动,每次都只能移到相邻的空位,每次需要花费一分钟,求从起点到终点最少要多久。

输入描述

多组测试数据。
一组测试测试数据表示一个三维迷宫:
前三个数,分别表示层数、一个面的长和宽,后面是每层的平面图。前三个数据为三个零表示结束。
层数不超过20,长和宽均不超过120。  

输出描述

如果能到输出“Escaped in 步数 minute(s).”

如果不能到输出“Trapped!”

样例输入
3 4 5
S....
.WWW.
.WW..
WWW.W
WWWWW
WWWWW
WW.WW
WW...
WWWWW
WWWWW
W.WWW
WWWWE
1 3 3
SWW
WEW
WWW
0 0 0
样例输出
Escaped in 11 minute(s).
Trapped!
提示

对于题目给出数据的含义就是输入l,r,c,分别代表迷宫有l层,每层长宽分别是r,c。
层数不超过20,长和宽均不超过120。
对于数据以可以这样移动:
(1,1,1)->(1,1,2)->(1,1,3)->(1,1,4)->(1,1,5)->(1,2,5)->
(1,3,5)->(1,3,4)->(1,4,4)->(2,4,4)->(2,4,5)->(3,4,,5)
共11步就可以到达终点 对于数据二明显不能到达,则输出Trapped!
这题用BFS解,每次去队首元素,如果是终点则输出结果移动的次数,否则,从该点开始分别向东南西北上下移动(如果可以走的话)并继续搜,如果到队列为空还没搜到解法,则说明无解。

代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n,m,w,c,r,f,cc,rr,ff;
char a[25][125][125];
int vis[25][125][125],cnt,step[25][125][125];
int dx[6]={-1,1,0,0,0,0};
int dy[6]={0,0,-1,1,0,0};
int dz[6]={0,0,0,0,-1,1};
void bfs(){
	queue<int> qx,qy,qz;
	qx.push(c);
	qy.push(r);
	qz.push(f);
	vis[f][c][r]=1;
	while(qx.empty()==0){
		int x=qx.front();
		int y=qy.front();
		int z=qz.front();
		qx.pop();
		qy.pop();
		qz.pop();
		if(x==cc&&y==rr&&z==ff){
			printf("Escaped in %d minute(s).\n",step[z][x][y]);
			return ;
		}
		for(int i=0;i<6;i++){
			int nx=x+dx[i];
			int ny=y+dy[i];
			int nz=z+dz[i];
			if(nx<=n&&nx>=1&&ny<=m&&ny>=1&&nz<=w&&nz>=1&&vis[nz][nx][ny]==0&&a[nz][nx][ny]!='W'){
				qx.push(nx);
				qy.push(ny);
				qz.push(nz);
				vis[nz][nx][ny]=1;
				step[nz][nx][ny]=step[z][x][y]+1;
			} 
		}
	}
	printf("Trapped!\n");
}
int main(){
	while(cin>>w>>n>>m){
		if(n==0&&m==0&&w==0){
			break;
		}
		memset(vis,0,sizeof vis);
		memset(step,0,sizeof step);
		for(int k=1;k<=w;k++){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					cin>>a[k][i][j];
					if(a[k][i][j]=='S'){
						c=i;
						r=j;
						f=k;
					}
					if(a[k][i][j]=='E'){
						cc=i;
						rr=j;
						ff=k;
					}
				}
			}
		}
		bfs();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值