程序设计思维Week2-作业

程序设计思维Week2-作业

A-Maze

问题描述

输入一个5x5的二维数组由0、1组成,表示法阵地图。地图显示,0表示可以走,1表示不可以走,左上角是起点,右下角是终点,这两个位置保证为0。编写程序,找到起点到终点的最短路线。
要求输出若干行,表示从左上角到右下角的最短路径依次经过的坐标。数据保证有唯一解。

解法分析

该题是典型的BFS算法应用。由于题目要求输出最短路径依次经过的坐标,需要定义一个Point类的path数组用于记录路径,vis数组用于记录某个点是否已经遍历到。在扩展过程中,每个点可以向上下左右四个方向进行扩展,但这里需要注意的是,在BFS过程中并不是每一个遍历到的点都是在最短路径上的,因此在定义Point类时,需要额外的pre用于记录到达该点的路径上它之前的那个点在path数组中的位置。
不停地进行扩展直到遇到终点,利用终点的pre向前递归找到最短路径并输出坐标。

总结

该题属于BFS算法的简单变形,需要熟练掌握BFS的基本结构。
需要注意的是:

  1. BFS的扩展是遍历了所有可到达的点,但很多其他点并不在两点间的最短路径上,因此记录到这些点时应回溯到他之前的那个点。
  2. 对每个点在path数组中的位置确定,需要front、end,front用于确定该点之前的点的位置,end用于记录该点在数组中的位置
  3. 遍历的判断条件:坐标不应超出5x5且可到达而还未遍历到的点。遍历到终点后及时停止扩展。

代码

#include <iostream>
#include<queue>
using namespace std;
bool vis[5][5];
int a[5][5];

struct point {
	int x;
	int y;
	int pre;
	point() { }
	point(int a, int b) {
		x = a; y = b;
	}
	point(int a, int b, int p) {
		x = a; y = b; pre = p;
	}

};

queue<point> Q;
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
point path[25];
//回溯
void print(point n) {
	if (n.pre != -1)print(path[n.pre]);
	printf("(%d, %d)\n", n.x, n.y);
}

void bfs() {
	memset(vis, 0, sizeof(vis));
	int front = 0, end = 0;
	Q.push(point(0, 0,-1));
	point next,now;
	vis[0][0] = 1;
	path[end++] = point(0, 0,-1);
	while (!Q.empty()) {
		now = Q.front();
		Q.pop();
		front++;
		if (now.x == 4 && now.y == 4) {
			print(now); return;
		}
		//int count = 0;
		for (int i = 0; i < 4; ++i) {
			int x = now.x + dx[i], y = now.y + dy[i];
			if (x < 0 || x>4 || y < 0 || y>4 || a[x][y] == 1)continue;
			else if (!vis[x][y]) {
				vis[x][y] = 1;
				next.x = x, next.y = y;
				next.pre = front - 1;		//记录前一个节点
				path[end++] = next;
				Q.push(next);
			}
			
		}
	}


}

int main()
{
	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 5; j++)
		{
			cin >> a[i][j];
		}
	bfs();

}

B-Pour Water

问题描述

倒水问题:两个水杯,最大容量分别为A和B,起始两个水杯中都是空的,我们希望通过一系列操作得到容量为C的水。操作仅限于: 倒满A/B杯,倒空A/B杯,把A的水倒到B中或反之。
要求输出获得C体积水的每一个操作,“fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。直到获得C体积的水,输出“success”。
ps:本题答案可能存在多解

解法分析

由于题目要求输出每步操作,因此我们需要自定义Status类,其中包括具体操作对应的序号以及本次操作后A和B杯中水的体积。类似于迷宫问题,我们可以把从起始状态到获得容量为C的水的一系列操作看成一条路径,即我们需要找到一条可以成功到达终点的路径,而路径可能有多条,所以可以使用BFS算法进行扩展,扩展方向为几种不同的操作。而同样,并不是扩展出的每一步操作都可以最终获得想要的结果,因此需要map类的from数组记录每一个状态与前一个状态的对应关系。
在扩展过程中使用队列进行BFS,根据不同的操作进行讨论直到获得想要的结果。再根据from数组递归至起始状态根据操作序号输出具体操作。

总结

该题属于隐式图问题,同样可以用BFS扩展,需要熟练掌握BFS的基本结构。
需要注意的点:

  1. 对于每一种状态来说只对应一种前状态,即在map类中该状态作为Key不可对应多个Value
  2. 并不是每一种状态最终都可以获得想要的结果,因此在遇到这些状态时应回溯到他之前的点然后继续其他的扩展,即摈弃无用的操作。

代码

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

struct Status
{
	int a, b,m;
	bool operator<(const Status &s) const
	{
		return a != s.a ? a < s.a : b < s.b;
	}
};
queue<Status> Q;
map<Status, Status> from;

void print(Status &s) {
	if (s.m == -2)return;
	print(from[s]);
	if (s.m == 1)printf("empty A\n");
	else if(s.m==2)printf("empty B\n");
	else if(s.m==3)printf("fill A\n");
	else if (s.m == 4||s.m==5)printf("pour B A\n");
	else if (s.m == 6)printf("fill B\n");
	else if (s.m == 7||s.m==8)printf("pour A B\n");

}

void refresh(Status &s, Status &t) {
	if (from.find(t) == from.end()) {
		from[t] = s;
		Q.push(t);
	}
}

void bfs(int A, int B, int C)
{
	Status s, t;
	s.a = 0; s.b = 0; s.m = -2;
	Q.push(s);

	while (!Q.empty())
	{
		s = Q.front();
		Q.pop();
		if (s.a == C || s.b == C) {
			
			print(s); 
			printf("success\n");
			return;
		}
		//倒空a的水
		if (s.a > 0) {
			t.a = 0;
			t.b = s.b;
			t.m = 1;
			refresh(s, t);
		}
		//倒空b的水
		if (s.b > 0) {
			t.b = 0;
			t.a = s.a;
			t.m = 2;
			refresh(s, t);
		}
		//续满a
		if (s.a < A)
		{
			t.a = A;
			t.b = s.b;
			t.m = 3;
			refresh(s, t);

			if (s.b != 0) {
				if (s.a + s.b <= A) {
					t.a = s.a + s.b;
					t.b = 0;
					t.m = 4;
					refresh(s, t);
				}
				else {
					t.a = A;
					t.b = s.a + s.b - A;
					t.m = 5;
					refresh(s, t);
				}
			}
		}

		//续满b
		if (s.b < B)
		{
			t.a = s.a;
			t.b = B;
			t.m = 6;
			refresh(s, t);
			if (s.a != 0)
			{
				if (s.a + s.b <= B)
				{
					t.a = 0;
					t.b = s.a + s.b;
					t.m = 7;
					refresh(s, t);
				}
				else {
					t.a = s.a + s.b - B;
					t.b = B;
					t.m = 8;
					refresh(s, t);
				}
			}
		}

	}
	//printf("-1/n");
}


int main()
{
	int a, b, c;
	while (cin >> a >> b >> c)
	{
		bfs(a, b, c);
	}
	return 0;
}


发布了21 篇原创文章 · 获赞 5 · 访问量 789

猜你喜欢

转载自blog.csdn.net/weixin_44578615/article/details/104653496
今日推荐