山东大学程序设计第二周作业

程序设计第二周作业

第一题

东东有一张地图,想通过地图找到妹纸。地图显示,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)

题目思路

这个题思路大方向没啥问题,搜索题,就是测码力,因为先讲的bfs,就用bfs实现

提炼一下这个入门搜索题代码的要点:
  • 整一个用于记录距离的数组,这样遍历后就可以得知每一个点到出发点的距离了(这题好像用不上)
  • 记录障碍点或者已经走过的点,可以在原数组上进行,也可以再开一个bool数组
  • 表示移动的数组,可以用pair,也可以单开两个int数组,可以表示上下左右移动就可
  • 保存路径,可以自己整一个结构体,也可以用pair的二位数组,下标对应点,存到达这里的最近的点
  • 加入队列的点的条件:未越界,且从未来过
  • 我是从终点向起点搜索的,这样可以直接输出路径
串一下整体思路:

从起始点出发,加表示移动的数组,每一个加后的点判断:是否越界,是否来过,没有则加入队列,循环上述过程,成功结束的条件为到达终点,失败的条件为队列为空

代码实现

#include<cstdio>
#include<queue>
#include <iostream>
using namespace std;
int map[10][10];
int vis[10][10], d[10][10];
int dx[] = { -1,1,0,0 };//上,下,左,右
int dy[] = { 0,0,-1,1 };
struct Node
{
	int x, y;//也可以在Node中加一个int mem属性,然后做一个全局的nodes,就不用mem[][]数组了.
	Node(int x, int y) :x(x), y(y) {}
	Node() {}
}mem[10][10];

int main()
{
	queue<Node> Q;
	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 5; j++)
			scanf("%d", &map[i][j]);
	for (int i = 0; i < 5; i++)
		for (int j = 0; j < 5; j++)
			vis[i][j] = 0;
	d[4][4] = 0;
	vis[4][4] = 1;
	Q.push(Node(4, 4));//这里是终点到起点的搜索,可以不用栈再输出
	while (!Q.empty())
	{
		Node node = Q.front(); Q.pop();
		int xx = node.x, yy = node.y;
		for (int i = 0; i < 4; i++)
		{
			int nx = xx + dx[i];
			int ny = yy + dy[i];
			if (nx >= 0 && nx < 5 && ny >= 0 && ny < 5 && vis[nx][ny] == 0 && map[nx][ny] == 0)
			{
				vis[nx][ny] = 1;
				Q.push(Node(nx, ny));
				d[nx][ny] = 1 + d[xx][yy];
				mem[nx][ny] = Node(xx, yy);
				if (nx == 0 && ny == 0) break;
			}
		}
	}
	int xx = 0, yy = 0;
	cout<<"(0, 0)"<<endl;
	while (true)
	{
		if (xx == 4 && yy == 4) break;
		printf("(%d, %d)\n", mem[xx][yy].x, mem[xx][yy].y);
		int a = xx, b = yy;
		xx = mem[a][b].x;
		yy = mem[a][b].y;

	}
}

第二题

倒水问题 “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)程序来判定你写的代码是否正确。

题目思路

这个题是一个隐式图问题,每一次倒水后杯子的状态可以被视为一个图的节点,最后的目标也可以视为一个节点,我们可以用bfs来搜索有没有从起点到终点的路。

  • 创建杯子类
  • 抽象倒水这种行为,倒水的情况是有限的,分别为:
    pour A B
    pour B A
    fill A
    fill B
    empty A
    empty B
  • 存储成功的路径
  • 加入队列的节点状态
  • 存储是否已经经历过当前状态
  • 去除非法操作(A中无水但是有A向B倒水的操作)

考虑的情况与第一题类似,不过用于抽象的数据类型不同
串一下思路:
从开始状态倒水,循环六种操作(去除非法操作),判断操作后的状态是否出现过,未出现过则加入队列,循环上述操作,成功的条件为到达目标状态,失败的条件为队列已空

代码实现

#include<iostream>
#include<queue>
#include<map> 
#include<stdio.h>
using namespace std;
int a, b, c;
string message[7] = { "success", "pour A B","pour B A","fill A","fill B","empty A","empty B" };

struct CUP {
	int cup1, cup2;
	CUP() {}
	CUP(int a, int b) { cup1 = a; cup2 = b; }
	bool operator<(const CUP& s) const { return cup1 != s.cup1 ? cup1 < s.cup1 : cup2 < s.cup2; }
	bool operator!=(const CUP& s) const { return cup1 != s.cup1 || cup2 != s.cup2; }
	CUP FillA() { CUP c(a, cup2); return c; }
	CUP FillB() { CUP c(cup1, b); return c; }
	CUP EmptyA() { CUP c(0, cup2); return c; }
	CUP EmptyB() { CUP c(cup1, 0); return c; }
	CUP AtoB() {
		CUP c;
		c.cup1 = max(cup1 + cup2 - b, 0);
		c.cup2 = min(cup1 + cup2, b);
		return c;
	}
	CUP BtoA() {
		CUP c;
		c.cup2 = max(cup1 + cup2 - a, 0);
		c.cup1 = min(cup1 + cup2, a);
		return c;
	}
};
struct tcups {
	CUP x1, y1;
	bool operator<(const tcups& s) const { return x1 != s.x1 ? x1 < s.x1 : y1 < s.y1; }
	tcups(CUP a, CUP b): x1(a),y1(b){}
};
queue<CUP> q;
map<CUP, CUP> mem;//存后一个状态的
map<CUP, bool> judge;//是否经历过该状态
map<tcups, int> otpt;//用来输出对两个杯子的操作
void output(CUP s) {
	CUP a = mem[s];
	if (mem[a] != a)//
		output(a);
	int b = otpt[tcups(s, a)];
	cout << message[b] << endl;
}
int main() {
	while (scanf("%d%d%d", &a, &b, &c) != EOF)
	{
		while (!q.empty())q.pop();
		judge.clear();
		CUP s(0, 0);
		mem[s] = s;
		q.push(s);
		judge[s] = true;
		while (!q.empty())
		{
			CUP now = q.front();
			q.pop();
			if (now.cup1 == c || now.cup2 == c)
			{
				output(now);
				cout << message[0] << endl;
				break;
			}
			CUP temp = now.AtoB();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 1; temp = now.BtoA();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 2; temp = now.FillA();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 3; temp = now.FillB();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 4; temp = now.EmptyA();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 5; temp = now.EmptyB();
			if (!judge[temp]) { judge[temp] = true; mem[temp] = now; q.push(temp); }
			otpt[tcups(temp, now)] = 6;
		}
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值