用类似BFS、Dijkstra的算法遍历状态图。
三个杯子水量之和不变,所以状态空间是二维而不是三维的。vis数组只需要开200*200,开得下。
状态图的边权是非负的,所以用优先队列可以保证对每个结点的第一次访问都是走的最短路径。
#include <iostream>
#include <cstring>
#include <queue>
#include <cassert>
using namespace std;
const int INF = 0x3f3f3f3f;
const int M = 200 + 10;
const int pour[][2] = {{0, 1}, {0, 2}, {1, 0}, {1, 2}, {2, 0}, {2, 1}};
struct Node {
int w[3], dist;
Node(int c): dist(0) {
w[0] = w[1] = 0;
w[2] = c;
}
bool operator < (const Node &p) const {
return dist > p.dist;
}
};
int cap[3], d;
int ans[M];
bool vis[M][M];
void update(Node &p) {
for(int i = 0; i < 3; i++)
ans[p.w[i]] = min(ans[p.w[i]], p.dist);
}
Node move(int p, int q, Node u) {
int k = min(cap[q] - u.w[q], u.w[p]);
u.w[p] -= k;
u.w[q] += k;
u.dist += k;
return u;
}
int main() {
int T; cin >> T;
while(T--) {
cin >> cap[0] >> cap[1] >> cap[2] >> d;
memset(ans, 0x3f, sizeof(ans));
memset(vis, 0, sizeof(vis));
priority_queue<Node> q;
q.push(Node(cap[2]));
vis[0][0] = true;
while(!q.empty()) {
Node u = q.top(); q.pop();
update(u);
for(int i = 0; i < 6; i++) {
Node v = move(pour[i][0], pour[i][1], u);
if(vis[v.w[0]][v.w[1]]) continue;
q.push(v);
vis[v.w[0]][v.w[1]] = true;
}
}
while(ans[d] == INF) d--;
assert(d >= 0);
cout << ans[d] << ' ' << d << endl;
}
return 0;
}