第一感觉:BFS。
对于每一种状态有6种可供选择的操作:
1.Fill A;
2.Fill B;
3.Pour A B;
4.Pour B A;
5.Empty A;
6.Empty B。
首先,有两个搜索起点:fill A和fill B。然后,枚举每一种可行的操作,入队,判断是否到达目标状态,到达则结束搜索,否则继续搜索。
这里要记录一下搜索到该状态的父亲节点以及是用哪种操作到达该状态的。
不过这样搜索下去的话,这棵树会越来越大,如果不能尽快到达目标状态的话,可能会爆栈。既然是BFS,那么到达目标状态的方式一定是最快的,只是在此之前,不知道会有多少状态,可行性有待验证。
代码(记忆体区段错误,不明白是什么东东):
#include<iostream> #include<cstring> using namespace std; struct condition { int a,b; int father,mean; }; int A,B,N; condition Q[10000]; void ready() { for (int i=0; i<10000; i++) Q[i].a = Q[i].b = Q[i].father = Q[i].mean = 0; } void Print(int x) { if (x == -1) return; else Print(Q[x].father); switch (Q[x].mean) { case 1:cout<<"fill A"<<endl;break; case 2:cout<<"fill B"<<endl;break; case 3:cout<<"pour A B"<<endl;break; case 4:cout<<"pour B A"<<endl;break; case 5:cout<<"empty A"<<endl;break; case 6:cout<<"empty B"<<endl;break; } } void BFS() { if (B == N) { cout<<"fill B"<<endl; cout<<"success"<<endl; } ready(); int head = 0, tail = 0; Q[tail].a = A; Q[tail].father = -1;Q[tail].mean = 1; tail++; Q[tail].b = B; Q[tail].father = -1;Q[tail].mean = 2; tail++; while (head<tail) { condition con; con = Q[head]; head++; if (con.a < A) //fill A { condition buf = con; buf.a = A; buf.mean = 1; buf.father = head-1; Q[tail] = buf; tail++; } if (con.b < B) //fill B { condition buf = con; buf.b = B; buf.mean = 2; buf.father = head-1; Q[tail] = buf; tail++; } if (con.a > 0 && con.b < B) //pour A B { condition buf = con; int tmp = min(buf.a,B - buf.b); buf.a -= tmp; buf.b += tmp; buf.father = head-1; buf.mean = 3; Q[tail] = buf; if (buf.b == N) { Print(tail); break; } tail++; } if (con.b > 0 && con.a <A) //pour B A { condition buf = con; int tmp = min(buf.b,A - buf.a); buf.b -= tmp; buf.a += tmp; buf.father = head-1; buf.mean = 4; Q[tail] = buf; if (buf.b == N) { Print(tail); break; } tail++; } if (con.a > 0) //empty A { condition buf = con; buf.a = 0; buf.father = head-1; buf.mean = 5; Q[tail] = buf; tail++; } if (con.b > 0) //empty B { condition buf = con; buf.b = 0; buf.father = head-1; buf.mean = 6; Q[tail] = buf; tail++; } } } int main() { while (cin>>A>>B>>N) { BFS(); cout<<"success"<<endl; } }
题解上给出了一个简便的方法:一直用A给B灌水,B灌满后就清空,然后继续用A给B灌水,一直到B到达目标状态。
#include <iostream> using namespace std; int main() { int A,B,N; while (cin>>A>>B>>N) { if (B == N) { cout<<"fill B"<<endl; cout<<"success"<<endl; continue; } int nowa = 0, nowb = 0; while (nowb != N) { for (int i=1; i<=(B-nowb)/A; i++) { cout<<"fill A"<<endl; cout<<"pour A B"<<endl; } nowb += A * ((B-nowb)/A); if (nowb == N) break; nowb = A - (B - nowb); cout<<"fill A"<<endl; cout<<"pour A B"<<endl; cout<<"empty B"<<endl; cout<<"pour A B"<<endl; } cout<<"success"<<endl; } return 0; }
关于灌水定理,有n个壶,容量为c[i]升,要用这n个壶倒出w升水。
对于能否用这n个壶倒出这些水,有下面两个条件:
1.w<=Σc[i];
2.w是gcd(c[1],c[2]......c[n])的倍数。
满足这两个条件,就可以倒出这些水。
(以下全部是对于互质的情况来说的,如果不互质,可以通过除以最大公约数得到互质。都是基于扩展欧几里得上的)
1.对于两个壶,只要一直用容量小的壶给容量大的壶灌水,满了就清空,再继续倒,一定会得w升的。
2.对于多个壶:
(1)如果能找到两个互质的壶,容量分别为c[1],c[2],则一定存在v1,v2,使得v1*[1]+v2*c[2]=1。那么只要直接灌满一些壶,剩下的零头,可以通过多次使用这两个壶倒出多个1升得到。
(2)如果找不到,就参考:http://wenku.baidu.com/view/01d7284c2b160b4e767fcf8f.html