Week2 作业 B - Pour Water

题目

倒水问题 “fill A” 表示倒满A杯,"empty A"表示倒空A杯,“pour A B” 表示把A的水倒到B杯并且把B杯倒满或A倒空。
输入包含多组数据。每组数据输入 A, B, C 数据范围 0 < A <= B 、C <= B <=1000
、A和B互质。程序的输出将由一系列的指令组成。这些输出行将导致任何一个罐子正好包含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

解题思路

首先输入数据有多种,而且事先并没有声明组数,因此需要用scanf("%d%d%d", &a, &b, &c) != EOF输入数据。每组输入的 A B C 分别表示 A杯容量 B杯容量 需要C单位的水 。我们每一次可以有6种操作:A装满水、B装满水、倒空A杯的水、倒空B杯的水、把A中的水倒进B中 和 把B中的水倒进A中。其中后两种的截至条件是一方倒满或一方倒空。

从初始状态开始有六种可能的操作,其中每一种操作都可能产生有效的操作,即会改变 A B 的值,产生一种新的状态。这又产生了一个问题,就是相同的状态,即 A B 的值可能由不同的操作流程产生,我们应该选取总步骤最少的操作流程。因此我们每产生一种可能的状态,都要判断这个状态是否已经产生过了,如果没有,才将这种新状态和产生它的状态保存下来。这样,我们才能通过产生 C 单位水时 A 和 B 的状态反向推出要产生结果状态的操作流程。

所以我们建立结构体 status 存储 A B 的状态,并重载小于运算符,使得我们可以直接比较结构体的大小。给出一个状态,我们要知道产生这种状态的前一个状态和操作步骤,我们可以通过 map 映射完成相关工作。 在程序中,map <status, status> history; 是状态到状态的映射,map<status, int> histype; 是状态到操作类型的映射。发现一种状态时,我们先通过 history.find(n) 判断这种状态之前是否已经产生过了,如果没有,则将这种映射关系加入相应的映射中。

要产生尽量短操作步骤,我们可以使用 BFS 搜索,从状态 { 0,0 } 开始,每次从队列中取出头元素,判断是否已经到达终点,如是调用 outprint 函数,通过递归查找末状态的前序状态,并将它们输出,否则就尝试六种可能的操作,把产生的新状态存储下来并加入到队列中。

程序代码

#include <iostream>
#include<queue>
#include<map>
#include<stdio.h>
using namespace std;

struct status { //存储状态
	int a, b;
	bool operator < (const status& s) const {
		if (a != s.a) return a < s.a;
		return b < s.b;
	}
};
string M[] = { "success\n","fill A\n","fill B\n","empty A\n","empty B\n","pour A B\n","pour B A\n" }; //操作类型


map<status, status> history; //前后序状态的映射
map<status, int> histype; //操作类型的映射
queue<status> q;

void record(status n, status s, int t) { //检查该状态是否产生过 & 存储状态
	if (history.end() != history.find(n)) return;
	if (n.a == 0 && n.b == 0) return;
	//	history.insert(pair<n, s>);
	history[n] = s;
	histype[n] = t;
	q.push(n);

}


void outprint(status st) { //递归输出
	if (history.find(st) == history.end()) { //到达递归终点
		return;
	}
	status curr = st;
	outprint(history[st]); //递归
	cout << M[histype[st]]; //输出
}
void output() { //递归输出
	outprint(q.front());
	return;
}

void pour(int a, int b, int c) { //模拟倒水操作

	status s{ 0,0 }; //初始状态
	q.push(s); //建立队列
	int type;
	while (!q.empty()) { //bfs
		s = q.front();
		if (s.a == c || s.b == c) { //到达终点
			outprint(s); //输出
			cout << M[0];
			return;
		}
		
		if (s.a < a) { //fill a
			type = 1;
			status n{ a, s.b };
			record(n, s, type);
		}
		if (s.b < b) { //fill b
			type = 2;
			status n{ s.a, b };
			record(n, s, type);
		}
		if (s.a > 0) { //empty a
			type = 3;
			status n{ 0, s.b };
			record(n, s, type);
		}
		if (s.b > 0) { //empty b
			type = 4;
			status n{ s.a, 0 };
			record(n, s, type);
		}
		//pour a b 
		if (s.a > 0) {
			type = 5;
			if (s.a + s.b >= b) { //倒满b
				status n{ s.a + s.b - b, b };
				record(n, s, type);
			}
			else { //倒空a
				status n{ 0, s.a + s.b };
				record(n, s, type);
			}
		}
		//pour b a
		if (s.b > 0) {
			type = 6;
			if (s.a + s.b >= a) { //倒满a
				status n{ a, s.a + s.b - a };
				record(n, s, type);
			}
			else { //倒空b
				status n{ s.a + s.b, 0 };
				record(n, s, type);
			}
		}
	}


}


int main()
{
	int a, b, c;
	while (scanf("%d%d%d", &a, &b, &c) != EOF) { //获取输入
		pour(a, b, c);
		while (!q.empty()) q.pop(); //清空本次使用的数据
		history.erase(history.begin(), history.end());
		histype.erase(histype.begin(), histype.end());
	}
	return 0;
}
发布了16 篇原创文章 · 获赞 2 · 访问量 311

猜你喜欢

转载自blog.csdn.net/ziseon/article/details/104690371