UVa10603 Fill
UVa10603 Fill
- 题目链接
- 题意
- 分析
- AC 代码
题目链接
UVa10603 Fill
题意
有装满水的6升的杯子、空的3升杯子和1升杯子,3个杯子中都没有刻度。在不使用其他道具的情况下,是否可以量出4升的水呢?方法如下图所示:(6,0,0)→(3,3,0)→(3,2,1)→(4,2,0)
注意:由于没有刻度,用杯子x给杯子y倒水时必须一直持续到把杯子y倒满或者把杯子x倒空,而不能中途停止。你的任务是解决一般性的问题:设3个杯子的容量分别为a, b, c,最初只有第3个杯子装满了c升水,其他两个杯子为空。最少需要倒多少升水才能让某一个杯子中的水有d升呢?如果无法做到恰好d升,就让某一个杯子里的水是d’升,其中d’<d并且尽量接近d。(1≤a,b,c,d≤200)。要求输出最少的倒水量和目标水量(d或者d’)。
分析
《入门经典》将此题作为例题,状态分析如下:
由于水的总量x永远不变,如果有两个状态的前两个杯子的水量都相同,则第3个杯子的水量也相同。换句话说,最多可能的状态数不会超过 2012=40401201^2=404012012=40401。
这个状态数量不大了,不过还有优化空间,抓住每次倒水必然有杯子被倒空或者倒满这一点,可以定义三维状态:记录倒空或者倒满的杯子编号(0、1、2)、是倒空还是倒满(0:空,1:满)以及其下一个杯子的水量(0~200)。那么总状态数才 3×2×201=12063\times 2\times201=12063×2×201=1206 。
用Dijkstra求最短路解法即可。
AC 代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;#define N 202
int f[3][2][N], c[3], v[3], d, ans, p; bool inq[3][2][N];
struct node {int s, f, w;void decode() {int x = s<2 ? s+1 : 0;v[s] = f ? c[s] : 0; v[x] = w; v[3-s-x] = c[2] - v[s] - v[x];}
};void solve() {cin >> c[0] >> c[1] >> c[2] >> d;ans = c[2] <= d ? c[2] : 0; p = 0;if (c[2] != d) {memset(f, -1, sizeof(f)); memset(inq, 0, sizeof(inq));queue<node> q; f[2][1][0] = 0; q.push({2, 1, 0});while (!q.empty()) {node t = q.front(); int &r = f[t.s][t.f][t.w]; t.decode(); q.pop(); inq[t.s][t.f][t.w] = false;for (int i=0; i<3; ++i) if (v[i]) for (int j=0; j<3; ++j) if (i != j) {int g, x, y, w[] = {v[0], v[1], v[2]};if (v[i] + v[j] > c[j]) g = r + c[j]-v[j], w[i] -= c[j]-v[j], w[j] = c[j], x = j, y = 1;else g = r + v[i], w[j] += v[i], w[i] = 0, x = i, y = 0;int z = w[x<2 ? x+1 : 0], &e = f[x][y][z];if (e < 0 || g < e) {e = g;for (int k=0; k<3; ++k) if (w[k]<=d && (w[k] > ans || (w[k]==ans && g<p))) ans = w[k], p = g;if (!inq[x][y][z]) q.push({x, y, z}), inq[x][y][z] = true;}}}}cout << p << ' ' << ans << endl;
}int main() {int t; cin >> t;while (t--) solve();return 0;
}