时间限制: 1 Sec 内存限制: 32 MB
提交: 222 解决: 0
[提交][状态][讨论版][命题人:外部导入]
题目描述
In the movie "Die Hard 3", Bruce Willis and Samuel L. Jackson were confronted with the following puzzle. They were given a 3-gallon jug and a 5-gallon jug and were asked to fill the 5-gallon jug with exactly 4 gallons. This problem generalizes that puzzle.
You have two jugs, A and B, and an infinite supply of water. There are three types of actions that you can use: (1) you can fill a jug, (2) you can empty a jug, and (3) you can pour from one jug to the other. Pouring from one jug to the other stops when the first jug is empty or the second jug is full, whichever comes first. For example, if A has 5 gallons and B has 6 gallons and a capacity of 8, then pouring from A to B leaves B full and 3 gallons in A.
A problem is given by a triple (Ca,Cb,N), where Ca and Cb are the capacities of the jugs A and B, respectively, and N is the goal. A solution is a sequence of steps that leaves exactly N gallons in jug B. The possible steps are
fill A
fill B
empty A
empty B
pour A B
pour B A
success
where "pour A B" means "pour the contents of jug A into jug B", and "success" means that the goal has been accomplished.
You may assume that the input you are given does have a solution.
输入
Input to your program consists of a series of input lines each defining one puzzle. Input for each puzzle is a single line of three positive integers: Ca, Cb, and N. Ca and Cb are the capacities of jugs A and B, and N is the goal. You can assume 0 < Ca <= Cb and N <= Cb <=1000 and that A and B are relatively prime to one another.
输出
Output from your program will consist of a series of instructions from the list of the potential output lines which will result in either of the jugs containing exactly N gallons of water. The last line of output for each puzzle should be the line "success". Output lines start in column 1 and there should be no empty lines nor any trailing spaces.
样例输入
3 7 1
9 32 6
样例输出
fill B
pour B A
empty A
pour B A
success
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
empty A
pour B A
fill B
pour B A
empty A
pour B A
empty A
pour B A
success
解题思路
- BFS适用于求解本题。因为BFS求解出的第一个解必是最短路径(最优解),DFS则不然。
- 本题具有马尔科夫性质,当前状态仅与上一状态和倒水动作有关。利用此特性,当碰到重复状态,不必入队列(相当于搜索中进行剪枝),大大降低计算时空复杂度。原本的计算复杂度时随着路径的长度指数增长的。
- BFS中,队列的使用是关键,保证搜索的有序性。但在此题中,我们还需输出最优路径,需保存路径信息,而STL队列没有此功能,一旦出列,路径结点就消失了。故笔者采用数组和队首队尾指针来构造队列(模拟pop(), front(), end()),这般的话,入队过的结点通通存储在数组里了,方便我们还原链表存储形式的路径。
坑
这两行代码 要保持严格的顺序,调换顺序后的计算结果不同。
a=a+b-cb;
b=cb;
AC代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1e5+10;
const int smaxn = 1010;
int table[smaxn][smaxn];
int front=0, last=0;
struct node{
int a, b;
int op;
int pre; //上一个状态
}tree[maxn]; //用数组的,线性存储结构来构造队列。
node manipulate(node now, int way, int ca, int cb){
int a = now.a;
int b = now.b;
node next;
switch(way){
case 1: a=ca; break; //fill A
case 2: b=cb; break; //fill B
case 3: a=0; break; //empty A
case 4: b=0; break; //empty B
case 5:if(a+b-cb>=0) {a=a+b-cb; b=cb;} //pour A B 判断溢出
else {b=a+b; a=0;} break;
case 6: if(a+b-ca>=0) {b=a+b-ca; a=ca;} //pour B A 判断溢出
else {a=a+b; b=0;} break;
}
next.a = a;
next.b = b;
next.op = way;
return next;
}
int BFS(int ca, int cb, int n){
//初始化
node ans[100];
memset(table, 0, sizeof(table));
front=last=0;
node S={0};
//在tree的基础上,添加两个指针,实现队列的操作。采用线性存储结构来构造队列。
//last指向队尾,front指向队首;
//S入队列
tree[last++] = S;
//当队列为空时中断循环,即队首指针与队尾指针 重合。
while(front != last){
node now = tree[front];
if(now.b == n) {
return front;
}
table[now.a][now.b] =1; //标记当前状态 为 访问过,入队过。
//对当前状态 执行6种操作,得到6个下一状态。
for(int i=1; i<=6; i++){
node next = manipulate(now, i, ca, cb);
next.pre = front;
if(table[next.a][next.b] == 0){
tree[last++] = next;
table[next.a][next.b] = 1; //标记当前状态 为 访问过。
}//当前状态,如果不曾访问,曾入队列 待阅兵。反之跳过。
}
//队首出列类比于pop(),以便得到队列的下一队首。
front++;
}
return -1;
}
char strs[7][20]={"", "fill A\n", "fill B\n", "empty A\n", "empty B\n", "pour A B\n", "pour B A\n"};
void print(int index){
if(index == -1) {
//printf("FAIL\n");
return;
}
stack<int> st;
//遍历操作序列,并入栈
while(index != 0){st.push(tree[index].op);index = tree[index].pre;}
while(!st.empty()){printf("%s", strs[st.top()]); st.pop();}
printf("success\n");
}
int main(){
int ca, cb, n;
while(~scanf("%d%d%d", &ca, &cb, &n)){
print(BFS(ca, cb, n));
}
return 0;
}