【代碼超詳解】POJ 3414 Pots(BFS,0 ms)

一、題目描述

Pots

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 29772 Accepted: 12428 Special Judge

Description

You are given two pots, having the volume of A and B liters respectively. The following operations can be performed:

FILL(i)      fill the pot i (1 ≤ i ≤ 2) from the tap;
DROP(i)      empty the pot i to the drain;
POUR(i,j)    pour from pot i to pot j; after this operation either the pot j is full (and there may be some water left in the pot i), or the pot i is empty (and all its contents have been moved to the pot j).

Write a program to find the shortest possible sequence of these operations that will yield exactly C liters of water in one of the pots.

Input

On the first and only line are the numbers A, B, and C. These are all integers in the range from 1 to 100 and C≤max(A,B).

Output

The first line of the output must contain the length of the sequence of operations K. The following K lines must each describe one operation. If there are several sequences of minimal length, output any one of them. If the desired result can’t be achieved, the first and only line of the file must contain the word ‘impossible’.

Sample Input

3 5 4

Sample Output

6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)

Source

Northeastern Europe 2002, Western Subregion

二、算法分析說明與代碼編寫指導

一共有六種操作:
“FILL(1)”,“DROP(1)”,“POUR(1,2)”,“FILL(2)”,“DROP(2)”,“POUR(2,1)”
可以採用 BFS 得到次數最少的操作序列之一。這種搜索可以看成在 6 叉樹上從根節點開始進行 BFS 求最短路。樹的根節點不含任何有效的信息,設其它節點都存儲如下信息:
操作類型 op(0 ~ 5)、進行該類型操作後兩個罐的水量 a,b、上一步操作的存儲位置 last。
樹可以按照層次遍歷的順序進行存儲,而且可以一邊進行 BFS 一邊構建。
題目要求給出具體的操作序列,所以我們不用 queue,而是改用 vector 存儲節點,因爲進行路徑還原更方便。
bool 型變量 f 代表是否找到了最短路徑。
六種操作並不是在任何時候都可以進行的。在搜索下一步之前要先判定操作是否可以正確進行。
FILL(1),只有在 1 罐不裝滿水的時候可以進行。
DROP(1),只有在 1 罐非空的時候可以進行。
POUR(1, 2),只有在 1 罐非空且 2 罐沒有裝滿時可以進行。
FILL(2),只有在 2 罐不裝滿水的時候可以進行。
DROP(2),只有在 2 罐非空的時候可以進行。
POUR(2, 1),只有在 2 罐非空且 1 罐沒有裝滿時可以進行。
節點 n 用於向 vector 插入新節點。在向節點 n 寫入信息時要注意:雖然有的操作只對 1 個罐進行(FILL / POUR),但必須向 n 一同寫入當前節點的另一個罐的信息,否則另一個罐的信息是來自上一個新插入的節點而不是當前正在搜尋的節點,從而導致給出錯誤的結果。
bitset v 用於刻畫一個狀態(在進行本次操作後)是否已經在序列中。如果已經存在,則不要添加,因爲這可能會導致最終找到的序列中,有若干段的操作是完全重複的,此時給出的步數自然不是最少的。但並不是在一出現重複的狀態就停止搜索並報告 impossible,因爲還有其它的操作可以嘗試(一開始就是沒想到這一點搞到我在樣例卡了特別久)。
當遍歷完已有的操作序列後,若仍未找到符合要求的狀態(1、2 兩個罐有一個的儲水量爲 c),則意味着不可能找到相應的方案,因爲在最後搜尋到的節點之後嘗試任何操作都會導致序列出現重複,也就是說順着搜索樹的任何一條從根到葉的路徑走,都會令兩個罐的水量隨着時間推移不斷循環但就是不爲 c。
設棧 r 用於儲存結果。當出現符合要求的狀態後,BFS 就可以終止,然後從序列的末端開始不斷根據節點的 last 值從後往前將相應的操作添加到棧中,然後從棧頂開始輸出即可。節點 0 不含任何有效信息,因此向前跳到 0 節點時應該停止往棧中壓入操作。

三、AC 代碼(0 ms)

注:本代碼採用 C++ 提交時 AC,G++ 提交時 WA,原因不明。

#include<cstdio>
#include<vector>
#include<stack>
#include<bitset>
#pragma warning(disable:4996)
using namespace std;
//struct node { unsigned op = 0, a = 0, b = 0; size_t last = 0; };
struct node { unsigned op, a, b; size_t last; };
const char* const op[] = { "FILL(1)","DROP(1)","POUR(1,2)","FILL(2)","DROP(2)","POUR(2,1)" };
unsigned a, b, c; vector<node> s; node n; stack<unsigned> r; bitset<101> v[101]; bool f = false;
inline bool check() { return n.a == c || n.b == c; }
int main() {
	scanf("%u%u%u", &a, &b, &c); s.push_back(n); v[0][0] = true;
	for (size_t i = 0; i < s.size(); ++i) {
		if (s[i].a < a) {
			n.op = 0; n.last = i; n.a = a; n.b = s[i].b;
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
		if (s[i].a != 0) {
			n.op = 1; n.last = i; n.a = 0; n.b = s[i].b;
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
		if (s[i].a != 0 && s[i].b < b) {
			n.op = 2; n.last = i;
			if (b - s[i].b > s[i].a) { n.b = s[i].b + s[i].a; n.a = 0; }
			else { n.a = s[i].a - (b - s[i].b); n.b = b; }
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
		if (s[i].b < b) {
			n.op = 3; n.last = i; n.b = b; n.a = s[i].a;
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
		if (s[i].b != 0) {
			n.op = 4; n.last = i; n.b = 0; n.a = s[i].a;
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
		if (s[i].b != 0 && s[i].a < a) {
			n.op = 5; n.last = i;
			if (a - s[i].a > s[i].b) { n.a = s[i].a + s[i].b; n.b = 0; }
			else { n.b = s[i].b - (a - s[i].a); n.a = a; }
			if (v[n.a][n.b] == false) { s.push_back(n); v[n.a][n.b] = true; }
		}
		if (check() == true) { f = true; break; }
	}
	if (f == true) {
		for (size_t i = s.size() - 1; i != 0; i = s[i].last) { r.push(s[i].op); }
		printf("%llu\n", r.size());
		while (r.empty() == false) { puts(op[r.top()]); r.pop(); }
	}
	else puts("impossible");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章