題目連結:POJ 3414 【Pots】
思路
對於每個A、B瓶的每個狀態,使用結構體儲存,同時pre儲存操作前的狀態的下標,方便回溯查詢正確路徑的操作,oper儲存使用什麼操作得到當前狀態,operNumber儲存到達當前狀態需要幾步。由於需要求的是最少的操作次數,所以使用BFS,依次增加操作次數,逐層搜尋,對於當前狀態分別進行六種操作:1.將A裝滿,2.將B裝滿,3.將A倒掉,4.將B倒掉,5.將A倒入B中,6.將B倒入A中。同時對於之前出現過的狀態,則continue,因為前面出現過的狀態,在前面出現時的操作次數肯定比現在要小,所以剪枝,沒出現過的狀態直接放入佇列中繼續操作,直到找出滿足題目要求的狀態為止。
程式碼
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
#define ll long long
const int N = 1e3 + 10;
int a, b, c, counts;
bool vis[N][N];
string oper[10] = {"", "FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"};
struct pot {
int a, b, pre, oper, operNumber, No;
bool operator<(const pot &c) const { return operNumber > c.operNumber; }
}pots[100010];
void answer(int x) {
int store[100010], cnt = 0;
// 找出正確路徑上的所有操作
while (x != -1) {
store[++cnt] = pots[x].oper;
x = pots[x].pre;
}
// 由於最後一個找出來的是初始節點,初始節點對應的操作是"",所以可以不輸出初始節點的操作
for (int i = --cnt; i > 0; i--) {
cout << oper[store[i]] << endl;
}
}
void bfs(struct pot start) {
priority_queue<pot> q;
q.push(start);
vis[start.a][start.b] = true;
start.No = counts;
pots[counts++] = start;
while (!q.empty()) {
pot pre = q.top();
// 判斷當前狀態是否滿足題目要求
if (pre.a == c || pre.b == c) {
cout << pre.operNumber << endl;
answer(pre.No);
return;
}
for (int i = 1; i <= 6; i++) {
pot now = pre;
// 把a倒滿
if (i == 1) {
now.a = a;
} else if (i == 2) {
now.b = b;
} else if (i == 3) {
now.a = 0;
} else if (i == 4) {
now.b = 0;
} else if (i == 5) {
if (now.a + now.b > b) {
now.a = now.a + now.b - b;
now.b = b;
} else {
now.b = now.a + now.b;
now.a = 0;
}
} else {
if (now.a + now.b > a) {
now.b = now.a + now.b - a;
now.a = a;
} else {
now.a = now.a + now.b;
now.b = 0;
}
}
// 噹噹前狀態每出現過時,標記當前狀態,並加入佇列中
if (vis[now.a][now.b] == false) {
now.oper = i;
now.operNumber = pre.operNumber + 1;
now.No = counts;
now.pre = pre.No;
pots[counts++] = now;
vis[now.a][now.b] = true;
q.push(now);
}
}
// 刪除當前狀態
q.pop();
}
// 輸出找不到方法的impossible
cout << "impossible" << endl;
}
int main() {
cin >> a >> b >> c;
bfs({0, 0, -1, 0, 0, 0});
return 0;
}