BFS(广度优先算法)—— pour water (A、B、C)

题目描述:

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

数据输入:

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

Sample input:

				2 7 5
				2 7 4

数据输出:

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

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

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


个人思路:

对于倒水问题,实在BFS 中一类特殊的存在——隐式图问题。它只给出了初始节点;目标节点以及扩展的条件等约束条件则隐含给出。
对于本题:初始节点是杯子为空:a = 0,b=0;目标是量出C单位的水,这里我认为目标节点是:a = C || b = C;下面最关键的BFS主体,考虑到题目限制对杯子的操作只有 fill or empty,两个杯子之间有 atob or btoa;所以对于两只杯子一共只有六种操作,而这六种操作就是BFS扩展的条件,操作后的新状态就是new node,然后判断是否push入队列就ok了。


数据结构:

struct cup {//杯子的状态
	int a, b, sign;//sign标记操作数{1,2,3,4,5,6}
	bool operator<(const cup& p)const {
		return a != p.a ? a < p.a : b < p.b;
	}
	bool operator == (const cup &q)const {
		return a == q.a || b == q.b;
	}
};
int A, B, C;//输入
cup s, t;//起终点
queue<cup> Q;//队列
map<cup, int> mp;//visit的作用
int mi = -1;//mp的指针
cup parent[100];//记录状态m的上一步操作parent[m]

在这里用的map来进行一个映射,首先是为了将cup类型转化为int型能够用于后面记录parent[mp[m]],这里注意到mi是mp的指针,理论上来说mp么得指针,至于它的具体作用在后面给出具体介绍。map的另一个作用在BFS中给出。


BFS

此BFS用mp.count(key)替代了以往visit[ ]的作用,用来作为判断push入队列的条件。

void BFS() {
	s.a = s.b = 0;
	s.sign = 0;
	t.a = t.b = C;
	int i = mi;	//mp的指针

	mp.clear();//清空mp
	while (!Q.empty())//清空队列,因为多组数据可能会产生冲突
		Q.pop();

	Q.push(s);
	mp[s] = ++i;//指针后移
	cup final;
	while (!Q.empty()) {
		cup now = Q.front();
		Q.pop();

		if (now.a < A) {//fill A
			cup n = now;
			fillA(n);
			
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 1;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.b < B) {//fill B
			cup n = now;
			fillB(n);	
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 2;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.a != 0 && now.b != 0) {//empty A or B
			cup n1 = now;
			emptyA(n1);
			if (mp.count(n1) == 0) {//n1不在mp中
				n1.sign = 3;//sign记录n1是经过了什么操作
				//cout << n1.sign << endl;
				Q.push(n1);			
				mp[n1] = ++i;//指针后移
				parent[mp[n1]] = now;//n1的父节点是now,而mp[n1]则是其指针
			}
			if (n1 == t) {//判定是否到达终点
				final = n1;
				break;
			}
			cup n2 = now;
			emptyB(n2);
			if (mp.count(n2) == 0) {//n2不在mp中
				n2.sign = 4;//sign记录n2是经过了什么操作
				//cout << n2.sign << endl;
				Q.push(n2);
				mp[n2] = ++i;//指针后移
				parent[mp[n2]] = now;//n2的父节点是now,而mp[n2]则是其指针
			}
			if (n2 == t) {//判定是否到达终点
				final = n2;
				break;
			}
		}
		if (now.b != B && now.a != 0) {//pour A B
			cup n = now;
			AtoB(n);	
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 5;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);	
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.a != A && now.b != 0) {//pour B A
			cup n = now;
			BtoA(n);
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 6;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);			
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}			
	}
	fprint(final);//打印操作
	print(final);
	cout << "success" << endl;
	//cout << "( " << final.a << " , " << final.b << " )" << endl;
}

递归逆序输出到达终点的操作:

void print(const cup& n) {//根据sign标记输出操作
	if (n.sign == 1)
		cout << "fill A" << endl;
	else if (n.sign == 2)
		cout << "fill B" << endl;
	else if (n.sign == 3)
		cout << "empty A" << endl;
	else if (n.sign == 4)
		cout << "empty B" << endl;
	else if (n.sign == 5)
		cout << "pour A B" << endl;
	else if (n.sign == 6)
		cout << "pour B A" << endl;
	else
		return;

}

void fprint(cup& m) {//递归倒序输出操作
	if (m.a == s.a && m.b == s.b)return;//出口
	cup p = parent[mp[m]];
	
	fprint(p);
	print(p);
}


实现代码:

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

struct cup {//杯子的状态
	int a, b, sign;//sign标记操作数{1,2,3,4,5,6}
	bool operator<(const cup& p)const {
		return a != p.a ? a < p.a : b < p.b;
	}
	bool operator == (const cup& q)const {
		return a == q.a || b == q.b;
	}
};
int A, B, C;//输入
cup s, t;//起终点
queue<cup> Q;//队列
map<cup, int> mp;//visit的作用
int mi = -1;//mp的指针
cup parent[100];//记录状态m的上一步操作parent[m]

//六种操作
void fillA(cup& m) {
	m.a = A;
}
void fillB(cup& m) {
	m.b = B;
}

void emptyA(cup& m) {
	m.a = 0;
}
void emptyB(cup& m) {
	m.b = 0;
}
void AtoB(cup& m) {
	if (m.a >= B - m.b) {
		int t = m.b;
		m.b = B;
		m.a = m.a - (B - t);
	}
	else {
		m.b = m.b + m.a;
		m.a = 0;
	}
}
void BtoA(cup& m) {
	if (m.b >= A - m.a) {
		int t = m.a;
		m.a = A;
		m.b = m.b - (A - t);
	}
	else {
		m.a = m.b + m.a;
		m.b = 0;
	}
}

void print(const cup& n) {//根据sign标记输出操作
	if (n.sign == 1)
		cout << "fill A" << endl;
	else if (n.sign == 2)
		cout << "fill B" << endl;
	else if (n.sign == 3)
		cout << "empty A" << endl;
	else if (n.sign == 4)
		cout << "empty B" << endl;
	else if (n.sign == 5)
		cout << "pour A B" << endl;
	else if (n.sign == 6)
		cout << "pour B A" << endl;
	else
		return;

}

void fprint(cup& m) {//递归倒序输出操作
	if (m.a == s.a && m.b == s.b)return;//出口
	cup p = parent[mp[m]];

	fprint(p);
	print(p);
}

void BFS() {
	s.a = s.b = 0;
	s.sign = 0;
	t.a = t.b = C;
	int i = mi;	//mp的指针

	mp.clear();//清空mp
	while (!Q.empty())//清空队列,因为多组数据可能会产生冲突
		Q.pop();

	Q.push(s);
	mp[s] = ++i;//指针后移
	cup final;
	while (!Q.empty()) {
		cup now = Q.front();
		Q.pop();

		if (now.a < A) {//fill A
			cup n = now;
			fillA(n);

			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 1;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.b < B) {//fill B
			cup n = now;
			fillB(n);
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 2;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.a != 0 && now.b != 0) {//empty A or B
			cup n1 = now;
			emptyA(n1);
			if (mp.count(n1) == 0) {//n1不在mp中
				n1.sign = 3;//sign记录n1是经过了什么操作
				//cout << n1.sign << endl;
				Q.push(n1);
				mp[n1] = ++i;//指针后移
				parent[mp[n1]] = now;//n1的父节点是now,而mp[n1]则是其指针
			}
			if (n1 == t) {//判定是否到达终点
				final = n1;
				break;
			}
			cup n2 = now;
			emptyB(n2);
			if (mp.count(n2) == 0) {//n2不在mp中
				n2.sign = 4;//sign记录n2是经过了什么操作
				//cout << n2.sign << endl;
				Q.push(n2);
				mp[n2] = ++i;//指针后移
				parent[mp[n2]] = now;//n2的父节点是now,而mp[n2]则是其指针
			}
			if (n2 == t) {//判定是否到达终点
				final = n2;
				break;
			}
		}
		if (now.b != B && now.a != 0) {//pour A B
			cup n = now;
			AtoB(n);
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 5;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
		if (now.a != A && now.b != 0) {//pour B A
			cup n = now;
			BtoA(n);
			if (mp.count(n) == 0) {//n不在mp中
				n.sign = 6;//sign记录n是经过了什么操作
				//cout << n.sign << endl;
				Q.push(n);
				mp[n] = ++i;//指针后移
				parent[mp[n]] = now;//n的父节点是now,而mp[n]则是其指针
			}
			if (n == t) {//判定是否到达终点
				final = n;
				break;
			}
		}
	}
	fprint(final);//打印操作
	print(final);
	cout << "success" << endl;
	//cout << "( " << final.a << " , " << final.b << " )" << endl;
}




int main() {

	while (~scanf_s("%d%d%d", &A, &B, &C)) {//VS中是scanf_s,和scanf效果相同
		BFS();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值