程序设计思维与实践 Week2 作业

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开始,且以左上角为原点。
  另外注意,输出中分隔坐标的逗号后面应当有一个空格。
  
思路与方法
可以采用bfs的方式。
设两个数组dx[]={0,0,1,-1}和 dy[]={1,-1,0,0}来进行上下左右移动。
并且设一个数组用来记录路径(记录前驱位置)。
从入口的第一个位置开始,依次遍历其上下左右位置(加边界判断),如果遍历到的位置可以走,将其标记,并记录该位置以及该位置来时的位置坐标(即当前走的这个位置是从哪个位置通过上下左右移动走过来的),直至到达终点。
通过递归输出路径。
代码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<cmath>
#include<string>
#include<queue>
using namespace std;



int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };


struct address
{
	int first;//相当于x坐标(此处初写的时候没想到x和y,凑合着看)
	int second;//相当于y坐标
	address()
	{
		first = second = 0;
	}

	address(int x, int y)
	{
		first = x;
		second = y;
	}
	address& operator=(const address& rv)
	{
		first = rv.first;
		second = rv.second;
		return *this;
	}
};

void bfs(int map[5][5],int reach,address pre[5][5])
{//map是地图,用reach标记走过的位置,用pre来记录前驱位置坐标
	queue<address> q;
	q.push(address(0, 0));

	map[0][0] = reach;
	while (!q.empty())
	{
		address temp = q.front();
		q.pop();
		if (temp.first == 4 && temp.second == 4)//判断终点
		{
			return;
		}

		for (int i = 0; i < 4; i++)
		{
			address p(temp.first + dx[i], temp.second + dy[i]);//进行上下左右移动

			if (p.first >= 0 && p.first < 5 && p.second >= 0 && p.second < 5 && map[p.first][p.second] == 0)//边界判断以及判断该位置是否能走
			{
				map[p.first][p.second] = reach;//可以走进行标记
				q.push(p);//放入队列
				pre[p.first][p.second] = temp;//记录该位置的前驱位置

			}
		}
	}
}

void output(address pre[5][5],address p)//递归输出路径
{
	if (p.first == 0 && p.second == 0)
	{
		cout << "(0, 0)" << endl;
		return;
	}
	output(pre, pre[p.first][p.second]);
	cout << "(" << p.first << ", " << p.second << ")" << endl;
}



int main()
{
	int map[5][5] = { 0 };

	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			cin >> map[i][j];//输入地图
		}
	}

	

	
	address pre[5][5];
	bfs(map, 2, pre);
     
	address p;
	p.first = 4;
	p.second = 4;
	output(pre, p);

}

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)程序来判定你写的代码是否正确。
思路
将A,B杯里的水量看作是一个点(A,B),得到C单位的水,看作是到达点(A,C)或者是(C,B)(注意:这里的A,B代表的是A、B杯里面的水量)。
将"fill A”,“fill B”,“empty A”,“empty B”,“pour A B”,"pour B A"看作是点(A,B)可以移动的位置,类似于坐标点上的上下左右移动。

设A、B杯水的最大容量为MA、MB。设A杯有A单位水,B杯有B单位水。

编号命令影响执行条件
0fill AA=MA;B=BA!=MA
1fill BA=A;B=MBB!=MB
2pour A B如果MB-B<=A(即B杯剩余容量小于等于A杯中水的体积,即B杯会被倒满),则A=A-(MB-B);B=MB;如果MB-B>A(即A杯中的水可以全部导入B中),则A=0;B=B+AA!=0
3pour B A如果MA-A<=B(即A杯剩余容量小于等于B杯中水的体积,即A杯会被倒满),则A=MA;B=B-(MA-A);如果MA-A>B(即B杯中的水可以全部导入A中),则A=A+B;B=0B!=0
4empty AA=0;B=BA!=0
5empty BA=A;B=0A!=0

在该过程中,我利用了map来记录倒水后的状态,即进行一次命令后得到的A、B杯中水的状态是否已经得到过(类似于地图上该点是否已经到达过),如果之前得到过该状态则放弃该次倒水,否则进行该次倒水,并记录得到该状态的前一个状态(即该状态是由哪个状态通过哪个命令得到的)。直至得到C单位的水。
代码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<cmath>
#include<string>
#include<queue>
#include<map>
using namespace std;

struct cup
{
	int A;//A杯
	int B;//B杯
	int o;//指令编号

	cup()
	{
		A = B = 0;
		o = -1;
	}

	cup(int A, int B, int o)
	{
		this->A = A;
		this->B = B;
		this->o = o;
	}

	bool operator<(const cup& rv)const
	{
		if (A != rv.A)return A < rv.A;
		return B < rv.B;
	}
};

string order[6] = { "fill A","fill B","pour A B","pour B A","empty A","empty B" };//存储指令

map<cup, cup> from;//判断该状态是否已经得到过,并记录该状态的前驱状态
queue<cup> q;

void JudgeInsert(cup& a, cup& b)
{
	if (from.find(b) == from.end())
	{//如果没有得到过该状态
		from[b] = a;//记录前驱状态
		q.push(b);//保存当前状态
	}
}

void output(cup &a)
{
	if ((a.A == 0&&a.B == 0)||from.find(a)==from.end())
	{
		if (a.o != -1)
		{
			//cout << a.A << "  " << a.B << "  " << order[a.o] << endl;
			cout << order[a.o] << endl;
		}
		return;
	}

	output(from[a]);
	if (a.o != -1)
	{
		
		//cout << a.A << "  " << a.B << "  " << order[a.o] << endl;
		cout << order[a.o] << endl;
	}
	//cout << a.A << "  " << a.B << "  " << order[a.o] << endl;
}

void PourWater(int A,int B,int C)
{
	//q.push(cup(0, 0, -1));
	cup temp1(0, 0, -1);
	cup temp2(0, 0, -1);
	q.push(temp1);
	while (!q.empty())
	{
		temp1 = q.front();
		q.pop();

		if (temp1.A == C || temp1.B == C)//判断z终点
		{
			output(temp1);
			return;
		}

		//empty A
		if (temp1.A > 0)
		{
			temp2.A = 0;
			temp2.B = temp1.B;
			temp1.o = 4;
			JudgeInsert(temp1, temp2);
		}

		//empty B
		if (temp1.B > 0)
		{
			temp2.A = temp1.A;
			temp2.B = 0;
			temp1.o = 5;
			JudgeInsert(temp1, temp2);
		}

		//fill A
		if (temp1.A < A)
		{
			temp2.A = A;
			temp2.B = temp1.B;
			temp1.o = 0;
			JudgeInsert(temp1, temp2);


		}

		//fill B
		if (temp1.B < B)
		{
			temp2.A = temp1.A;
			temp2.B = B;
			temp1.o = 1;
			JudgeInsert(temp1, temp2);
		}



		//pour A B   直到B满或者A空
		if (temp1.A != 0)
		{
			//A会倒空
			if (B - temp1.B >= temp1.A)
			{
				temp2.B = temp1.B + temp1.A;
				temp2.A = 0;
				temp1.o = 2;
				JudgeInsert(temp1, temp2);
			}
			else
			{//B被倒满
				temp2.A = temp1.A - (B - temp1.B);
				temp2.B = B;
				temp1.o = 2;
				JudgeInsert(temp1, temp2);
			}
		}
		

		//pour B A
		if (temp1.B != 0)
		{
			//B会倒空
			if (A - temp1.A >= temp1.B)
			{
				temp2.A = temp1.A + temp1.B;
				temp2.B = 0;
				temp1.o = 3;
				JudgeInsert(temp1, temp2);
			}
			else
			{//A被倒满
				temp2.A = A;
				temp2.B = temp1.B - (A - temp1.A);
				temp1.o = 3;
				JudgeInsert(temp1, temp2);
			}
		}



	}

}




int main()
{
	int A, B, C;
	while (cin >> A >> B >> C)
	{
		from.clear();
		while (!q.empty()) 
		{
			q.pop();
		}
		PourWater(A, B, C);
		cout << "success" << endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值