【Week2 作业】A - Maze、B - Pour Water

A - Maze

题意:

东东有一张地图,想通过地图找到妹纸。地图显示,0表示可以走,1表示不可以走,左上角是入口,右下角是妹纸,这两个位置保证为0。既然已经知道了地图,那么东东找到妹纸就不难了,请你编一个程序,写出东东找到妹纸的最短路线。

Input

输入是一个5 × 5的二维数组,仅由0、1两数字组成,表示法阵地图。

Output

输出若干行,表示从左上角到右下角的最短路径依次经过的坐标,格式如样例所示。数据保证有唯一解。

Sample Input

0 1 0 0 0
0 1 0 1 0
0 1 0 1 0
0 0 0 1 0
0 1 0 1 0

Sample Output

(0, 0)
(1, 0)
(2, 0)
(3, 0)
(3, 1)
(3, 2)
(2, 2)
(1, 2)
(0, 2)
(0, 3)
(0, 4)
(1, 4)
(2, 4)
(3, 4)
(4, 4)

Hint

坐标(x, y)表示第x行第y列,行、列的编号从0开始,且以左上角为原点。
另外注意,输出中分隔坐标的逗号后面应当有一个空格。

思路做法:

由于要计算最短路线,直接用宽度优先搜索,从左上角开始将地图所有位置遍历。
对于任意一个位置(x,y),如果之前没有接触到,就把它更新——做上接触过的标记(dis二维数组初始为-1表示为接触,将对应位置的dis[x][y]记为父结点dis+1),记录父结点(b二维结构体数组),并放入队列中。当最终队列为空时,所有能便历到的地方都遍历过了。
最后返回右下角位置在b数组中的值(即“妹子”位置的父结点),定义print函数通过递归将路径输出。

总结:

很基础的一道广度优先搜索题目,不需要任何思维上的转化,直接上手就可以按题意完成。
需要注意的有宽搜时用了const dx[]和dy[]数组来实现4个方向的“移动”,并且if的边界判定、是否已接触过(一定不能重复遍历)、是否不能接触(地图的输入中1是不能走的)以及最后结果的输出用了递归(很巧妙的方法)。

代码:

#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
const int n=5;
int a[n][n];
int dis[n][n];
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
struct point{
	int x,y;
	point(){}
	point(int x,int y):x(x),y(y){}
};
point b[n][n];
queue<point> Q;
point bfs(){
	Q.push(point(0,0));
	dis[0][0]=0;
	while(!Q.empty()){
		point now=Q.front();
		Q.pop();
		for(int i=0;i<4;i++){
			int x=now.x+dx[i],y=now.y+dy[i];
			if(x>=0&&x<=n-1&&y>=0&&y<=n-1){
				if(dis[x][y]==-1&&!a[x][y]){
					dis[x][y]=dis[now.x][now.y]+1;
					b[x][y]=now;
					Q.push(point(x,y));
				}
			}
		}
	}
	return b[n-1][n-1];
}

void print(point p){
	if(p.x!=0||p.y!=0){
		print(b[p.x][p.y]);
	}
	cout<<"("<<p.x<<", "<<p.y<<")"<<endl;
}

int main(){
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			cin>>a[i][j];
	memset(dis,-1,sizeof(dis));
	point res=bfs();
	print(res);
	cout<<"(4, 4)"<<endl;
	return 0;
} 

B - Pour Water

题目:

倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。

Input

输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000 、A和B互质。

Output

你的程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含C单位的水。每组数据的最后一行输出应该是“success”。输出行从第1列开始,不应该有空行或任何尾随空格。

Sample Input

2 7 5
2 7 4

Sample Output

fill B
pour B A
success 
fill A
pour A B
fill A
pour A B
success

Notes

如果你的输出与Sample Output不同,那没关系。对于某个"A B C"本题的答案是多解的,不能通过标准的文本对比来判定你程序的正确与否。 所以本题由 SPJ(Special Judge)程序来判定你写的代码是否正确。

思路做法:

倒水是一种隐含图问题,看起来和图没多大关系,但可以抽象成图用广度优先搜索求得最优解。
将问题中的2个杯子抽象,定义为结构体beizi,作为图中的一个个结点,包括A、B杯中的水量以及表示本节点是从哪来的一个字段(为了应对题目要求的输出,初始结点不需要此字段)。接下来与A题中的解法大致相同,只不过是每个结点的移动和到达目标结点的判定需要自己定义。
在beizi中定义:倒满A杯、倒满B杯、倒空A杯、倒空B杯、A倒B、B倒A——这6个图中可按特定方向移动的方法。另外再重载beizi类的小于号,目的是用map建立2个映射:isFound——该结点是否已出现过(宽搜中不重复搜索)和father——该节点的父结点(为了还原最短路径)。
宽搜过程中对每个从队列中取出的结点,它的6个方向都要检查是否可加入队列中。为简化代码,可以将检查过程写为单独的函数。
最后,如果取出的结点中的2个杯子之一的水量是目标水量,就可以用递归的方法将路径(连带字段)输出。

总结:

隐含图问题只要能将原问题抽象转化为图的问题(主要是结点转移方向、目标节点判定等),就能应用广度优先搜索解答。

代码:

#include <iostream>
#include <queue>
#include <map>
#include <string>
#include <string.h>
using namespace std;
int A,B,C;
struct beizi{
	int a,b;
	string c;
	beizi(){}
	beizi(int _a,int _b):a(_a),b(_b){}
	beizi(int _a,int _b,string _c):a(_a),b(_b),c(_c){}
	bool operator<(const beizi& temp)const{
		if(a!=temp.a) return a<temp.a;
		return b<temp.b;
	}
	bool operator==(const beizi& temp)const{
		return a==temp.a&&b==temp.b;
	}
	beizi fillA(){
		return beizi(A,b,"fill A");
	}
	beizi fillB(){
		return beizi(a,B,"fill B");
	}
	beizi emptyA(){
		return beizi(0,b,"empty A");
	}
	beizi emptyB(){
		return beizi(a,0,"empty B");
	}
	beizi pourAtoB(){
		return (a+b>B)?beizi(a+b-B,B,"pour A B"):beizi(0,a+b,"pour A B");
	}
	beizi pourBtoA(){
		return (a+b>A)?beizi(A,a+b-A,"pour B A"):beizi(a+b,0,"pour B A");
	}
};
queue<beizi> Q;
map<beizi,bool> isFound;
map<beizi,beizi> father;

bool check(beizi child,beizi now){
	if(isFound[child]==0){
		isFound[child]=1;
		father[child]=now;
		Q.push(child);
	}
}

void print(beizi now){
	if(now==beizi(0,0)) return;
	print(father[now]);
	cout<<now.c<<endl;
}

void bfs(){
	beizi now(0,0);
	Q.push(now);
	isFound[now]=1;
	while(!Q.empty()){
		now=Q.front();
		Q.pop();
		if(now.a==C||now.b==C){
			print(now);
			return;  //找到目标 
		}
		check(now.emptyA(),now);
		check(now.emptyB(),now);
		check(now.fillA(),now);
		check(now.fillB(),now);
		check(now.pourAtoB(),now);
		check(now.pourBtoA(),now);
	}
	return;
}

int main(){
	while(cin>>A>>B>>C){
		isFound.clear();
		father.clear();
		while(!Q.empty()) Q.pop();
		bfs();
		cout<<"success"<<endl;
	} 
	return 0;
} 
发布了10 篇原创文章 · 获赞 0 · 访问量 231

猜你喜欢

转载自blog.csdn.net/weixin_44898140/article/details/104594836