分析:假设在某个时刻,第1个杯子中有v0升水,第2个杯子中有v1升水,第3个杯子中有v2升水,称当时的系统状态为(v0,v1,v2);把‘状态’看成图中的结点,可以得到状态转移图;由于无论如何倒,杯子中的水量都是整数,因此第3个杯子的水量最多只有0,1,2,3 ... ,c共c+1种可能,同理,第2个杯子的水量共有b+1种可能;而一旦两个杯子的水量一样了,则第三个杯子水量也一样,所以最多的状态不会超过 201^2=40401;所以该问题实际上就是bfs求图的最短路,只不过这里不是真的路径最短,而是水量最少。
1、初始化优先队列,把最初状态入队列
2、取出水量最小的结点,更新水量表
3、如果水量已经达到d则退出
4、循环遍历6种状态,计算当前状态移动的水量并生成新的结点
5、若新结点未访问则入队列,继续第4步
6、回到第2步
7、从d开始查水量表,如果水量非负则输出结果
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 200 + 5;
struct Node {
int v[3], cnt;
bool operator< (const Node& b) const { return cnt > b.cnt; }
};
int vis[maxn][maxn], ans[maxn], cap[3], d;
void update_ans(const Node& a)
{
for(int i=0; i<3; i++) {
int d = a.v[i];
if(ans[d] < 0 || a.cnt < ans[d]) ans[d] = a.cnt;
}
}
void solve()
{
memset(vis, 0, sizeof(vis));
memset(ans, -1, sizeof(ans));
priority_queue<Node> q;
Node st;
st.cnt = 0; st.v[0] = 0; st.v[1] = 0; st.v[2] = cap[2];
q.push(st);
vis[0][0] = 1;
while(!q.empty()) {
st = q.top(); q.pop();
update_ans(st);
if(ans[d] >= 0) break;
for(int i=0; i<3; i++) {
for(int j=0; j<3; j++) if(i!=j){
if(st.v[i]==0 || st.v[j]==cap[j]) continue;
int amount = min(cap[j], st.v[i]+st.v[j]) - st.v[j];
Node u;
memcpy(&u, &st, sizeof(st));
u.cnt = st.cnt + amount;
u.v[i] -= amount;
u.v[j] += amount;
if(!vis[u.v[0]][u.v[1]]) {
vis[u.v[0]][u.v[1]] = 1;
q.push(u);
}
}
}
}
while(d>=0) {
if(ans[d]>=0) {
printf("%d %d\n", ans[d], d);
return ;
}
d--;
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d%d", &cap[0], &cap[1], &cap[2], &d);
solve();
}
return 0;
}