3个大小为a,b,c的杯子,一开始只有c装了水,求是否能互相倒水,倒出d,如果可以,求最小倒水量,如果不能,求最接近d的d'。
以杯子水量(x,y,z)为状态,bfs即可。
因为总水量不变,vis[][]即可检查是否遍历过。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 200 + 5;
bool vis[maxn][maxn];
int ans[maxn];//ans[d]达到d水量时,最少的倒水量
struct state{
int w[3];
int dist;//已倒水量
bool operator < (const state &a) const{
return dist > a.dist;
}
};
void update_ans(state &s)
{
for (int i = 0; i < 3; i ++) {
if (ans[s.w[i]] == -1) {
ans[s.w[i]] = s.dist;
}
else ans[s.w[i]] = min(ans[s.w[i]],s.dist);
}
}
void solve(int a,int b,int c,int d)
{
memset(vis, 0, sizeof(vis));
memset(ans,-1,sizeof(ans));
int cap[3] = {a,b,c};
priority_queue<state> pq;
state start;
start.w[0] = 0,start.w[1] = 0,start.w[2] = c,start.dist = 0;
pq.push(start);
while (!pq.empty()) {
state t = pq.top();pq.pop();
vis[t.w[0]][t.w[1]] = true;
update_ans(t);
for (int i = 0; i < 3; i ++) {
for (int j = 0; j < 3; j ++) {
if (i != j) {
if (t.w[i] != 0 && t.w[j] != cap[j]) {//i向j倒水
state nt;
int pour = min(t.w[i],cap[j] - t.w[j]);
nt.w[i] = t.w[i] - pour;
nt.w[j] = t.w[j] + pour;
nt.w[3 - i - j] = t.w[3 - i - j];
nt.dist = t.dist + pour;
if (!vis[nt.w[0]][nt.w[1]]) {
pq.push(nt);
}
}
}
}
}
}
}
int main()
{
int T;
cin >> T;
while (T --) {
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
solve(a,b,c,d);
for (int i = d; i >= 0; i --) {
if (ans[i] != -1) {
printf("%d %d\n",ans[i],i);
break;
}
}
}
return 0;
}