这就是一道小学奥数倒水题(滑稽)
言归正传,首先注意到数据范围很小,只有200,那么可以把三个杯子里的水量作为一个状态进行暴力bfs,显然水的总量一定,只要知道前两个杯子有多少水这个状态就确定了,也就是说状态最多200x200种,很小的。
再考虑下贪心,到某个杯子水量为d的最小倒水量一定由前一个状态倒水量最小值搜索过来的,所以用一个优先队列存状态,按倒水量建个小根堆。
最后如果实现不了某个杯子水量为d,还需要找到一个最接近的。于是我们可以用一个ans数组存储当一个杯子水量为i时的最小值,那么到不了d的话只需要不断向下找ans数组总会有这样一个d被更新。
参考代码
#include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define inf 1e9 using namespace std; struct pos { int v[4]; int sum; bool operator < (const pos &a) const { return sum > a.sum; } }; int v[5],d,ans[210]; bool vis[210][210][210]; void bfs() { memset(vis,0,sizeof(vis)); memset(ans,-1,sizeof(ans)); priority_queue<pos>q; pos top,node; top.v[1] = top.v[2] = 0; top.v[3] = v[3]; top.sum = 0; vis[0][0][v[3]] = 1; q.push(top); while(q.size()) { top = q.top(); q.pop(); for(int i = 1;i <= 3;i++) { if(ans[top.v[i]] < 0 || ans[top.v[i]] > top.sum) ans[top.v[i]] = top.sum; } if(ans[d] > 0) return; for(int i = 1;i <= 3;i++) { for(int j = 1;j <= 3;j++) { if(i == j) continue; if(!top.v[j]) continue; pos tmp; if(top.v[j] + top.v[i] > v[i]) { tmp.v[i] = v[i]; tmp.v[j] = top.v[j] + top.v[i] - v[i]; tmp.v[6 - i - j] = top.v[6 - i - j]; tmp.sum = top.sum + v[i] - top.v[i]; } else { tmp.v[i] = top.v[i] + top.v[j]; tmp.v[j] = 0; tmp.v[6 - i - j] = top.v[6 - i - j]; tmp.sum = top.sum + top.v[j]; } if(!vis[tmp.v[1]][tmp.v[2]][tmp.v[3]]) { vis[tmp.v[1]][tmp.v[2]][tmp.v[3]] = 1; q.push(tmp); } } } } return; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d %d %d %d",&v[1],&v[2],&v[3],&d); bfs(); while(ans[d] == -1) d--; printf("%d %d ",ans[d],d); } return 0; }