算法竞赛入门经典 7-8 倒水问题 UVa10603(BFS/隐式图最短路)

题意:有三个容量a,b,c的无刻度杯子,其中前两个为空,第三个装满水。现让它们互相倒水(不能中途停下,只能在某个杯子的水倒空或接满时停下),最少需要倒多少水,能使其中一个杯子的水量达到d,如果无法达到d,就让某个杯中的水量d'小于且尽量接近d。输出最少倒水量和目标水量(d或d')。

 

如果问题变成"最少需要多少次",不难用bfs解决。

问题是"最小倒水量“,不妨先考虑一下怎么表示状态。

每个状态需要用四个量来表示(杯中的水量v0,v1,v2和目前的倒水量dist),根据题意模拟互相倒水的所有情况并更新状态。

直觉上,为了保证dist最小,可以把状态扔进以dist从小到大排序的单调队列里,此时每次取出的都很可能是当前dist对应的倒水量最少的状态, 用它更新dist对应的答案。直到结束。

为什么这样做是正确的?因为所有可能的状态构成了一个图,上述算法实际上是在状态图中跑了一遍dijkstra算法。

结束后从d开始往下找有解的目标水量输出即可。

 

 1 // by 刘汝佳 
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 
 5 struct Node{
 6   int v[3], dist;
 7   bool operator < (const Node& rhs) const {
 8     return dist > rhs.dist;
 9   }
10 };
11 
12 const int maxn = 200 + 10;
13 int vis[maxn][maxn], cap[3], ans[maxn];
14 
15 void update_ans(const Node &u) {
16   for (int i = 0; i < 3; i++) {
17     int d = u.v[i];
18     if (ans[d] < 0 || u.dist < ans[d]) ans[d] = u.dist;
19   }
20 }
21 
22 
23 void solve(int a, int b, int c, int d) {
24   cap[0] = a; cap[1] = b; cap[2] = c;
25   memset(vis, 0, sizeof(vis));
26   memset(ans, -1, sizeof(ans));
27   priority_queue<Node> q;
28   
29   Node start;
30   start.dist = 0;
31   start.v[0] = start.v[1] = 0; start.v[2] = c;
32   q.push(start);
33   vis[0][0] = 1;
34 
35   while (!q.empty()) {
36     
37     Node u = q.top(); q.pop();
38     update_ans(u);
39     if (ans[d] >= 0) break;
40     
41     for (int i = 0; i < 3; i++)
42       for (int j = 0; j < 3; j++) if (i != j) {
43         if (u.v[i] == 0 || u.v[j] == cap[j]) continue;
44         int amount = min(cap[j], u.v[i] + u.v[j]) - u.v[j];
45         Node u2;
46         memcpy(&u2, &u, sizeof(u));
47         u2.dist = u.dist + amount;
48         u2.v[i] -= amount;
49         u2.v[j] += amount;
50         if (!vis[u2.v[0]][u2.v[1]]) {
51           vis[u2.v[0]][u2.v[1]] = 1;
52           q.push(u2);
53         }
54       }
55   }
56   while (d >= 0) {
57     if (ans[d] >= 0) {
58       printf("%d %d\n", ans[d], d);
59       return;
60     }
61     d--;
62   } 
63 }
64 
65 int main() {
66   int T, a, b, c, d;
67   cin >> T;
68   while (T--) {
69     cin >> a >> b >> c >> d;
70     solve(a, b, c, d);
71   }
72   return 0;
73 }
View Code

 

posted @ 2021-08-06 17:04  _vv123  阅读(121)  评论(0编辑  收藏  举报