|  | 
|  | 1 | +# 毎日一题 -  水壶问题 | 
|  | 2 | + | 
|  | 3 | +## 信息卡片 | 
|  | 4 | + | 
|  | 5 | +* 时间:2019-09-15 | 
|  | 6 | +* 题目链接:https://leetcode-cn.com/problems/water-and-jug-problem/ | 
|  | 7 | +* tag:`Math`   | 
|  | 8 | +## 题目描述 | 
|  | 9 | +``` | 
|  | 10 | +给你一个装满水的 8 升满壶和两个分别是 5 升、3 升的空壶,请想个优雅的办法,使得其中一个水壶恰好装 4 升水,每一步的操作只能是倒空或倒满。 | 
|  | 11 | +``` | 
|  | 12 | + | 
|  | 13 | +## 参考答案 | 
|  | 14 | + | 
|  | 15 | +1.数学分析解答 | 
|  | 16 | + | 
|  | 17 | +上面的问题是一个特例,我们可以抽象为[leetcode-365-水壶问题](https://leetcode-cn.com/problems/water-and-jug-problem/)。 | 
|  | 18 | +``` | 
|  | 19 | +有两个容量分别为 x升 和 y升 的水壶(壶1,壶2)以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水? | 
|  | 20 | +``` | 
|  | 21 | + | 
|  | 22 | +解题核心思路(x < y,即壶1容量小于壶2,x == y的情况后面讨论): | 
|  | 23 | + | 
|  | 24 | +1. 将壶2倒满,往壶1倒入至满。 | 
|  | 25 | +2. 若壶1满,记录当前壶2中新水量。壶1倒出,将壶2中剩余的继续往壶1中倒入;(当壶1满,继续此操作,并记录当前壶2中新水量nw, 若此新水量已被记录,则)。 | 
|  | 26 | +3. 若出现壶1不满时(即此时壶2必空),重复操作1。 | 
|  | 27 | + | 
|  | 28 | +开辟一个新数组nws记录所有新水量,对任意nws[i],可构造的水量为nws[i],nws[i]+x,nws[i]+y。 | 
|  | 29 | + | 
|  | 30 | +(其实不需要新数组,因为数学上可以证明新水量的值循环周期呈现,故可以使用一个临时变量cur,当cur==x为终止条件) | 
|  | 31 | + | 
|  | 32 | +数学证明新水量nw值是循环周期的: | 
|  | 33 | + | 
|  | 34 | + | 
|  | 35 | +个别特殊情况考虑: | 
|  | 36 | + | 
|  | 37 | +- x == z || y == z;  **true** | 
|  | 38 | +- x == 0 || x+y < z;  **false** | 
|  | 39 | +- x+y == z || z == 0;  **true** | 
|  | 40 | + | 
|  | 41 | +```c++ | 
|  | 42 | +class Solution { | 
|  | 43 | +public: | 
|  | 44 | +    bool canMeasureWater(int x, int y, int z) { | 
|  | 45 | +        if(x > y) return canMeasureWater(y, x, z);  | 
|  | 46 | +        if(x == z || y == z) return true; | 
|  | 47 | +        if(x == 0 || x+y < z) return false; | 
|  | 48 | +        if(x+y == z || z == 0) return true; | 
|  | 49 | +        int cur = y - x; | 
|  | 50 | +        while(cur != x){ | 
|  | 51 | +            if(cur == z) return true; | 
|  | 52 | +            if(cur > x){ | 
|  | 53 | +                if(cur + x == z) return true; | 
|  | 54 | +                cur = cur - x; | 
|  | 55 | +            } | 
|  | 56 | +            else{ | 
|  | 57 | +                if(cur + y == z || cur + x == z) return true; | 
|  | 58 | +                cur = y - x + cur; | 
|  | 59 | +            } | 
|  | 60 | +        } | 
|  | 61 | +        return false; | 
|  | 62 | +    } | 
|  | 63 | +}; | 
|  | 64 | +``` | 
|  | 65 | +
 | 
|  | 66 | +2.BFS | 
|  | 67 | +
 | 
|  | 68 | +不仅可以计算是否能获取 z 升水,而且可以获得最少多少操作可获取 z 升水。(缺点,无法通过,因为需要太大的空间,需要申请一个三维数组记录状态) | 
|  | 69 | +
 | 
|  | 70 | +核心思想就是状态转移问题: | 
|  | 71 | +
 | 
|  | 72 | +壶0(x+y),壶1(x),壶2(y),壶0是本是无限大水池,同理于定义为大小为x+y的壶。用bfs的思想,使用一个队列记录所有新的状态。 | 
|  | 73 | +
 | 
|  | 74 | +对于任意状态(c,a,b),状态转移就是: | 
|  | 75 | +
 | 
|  | 76 | +- 若c不为0,将壶0倒水入壶1或壶2;若a不为0,将壶1倒水入壶0或壶2;若b不为0,将壶2倒水入壶0或壶1; | 
|  | 77 | +- 记录每个新状态,并入队,若此状态访问过则不入队。 | 
|  | 78 | +
 | 
|  | 79 | +特殊情况考虑同1。 | 
|  | 80 | +
 | 
|  | 81 | +```c++ | 
|  | 82 | +class Solution { | 
|  | 83 | +public: | 
|  | 84 | +    struct state{ | 
|  | 85 | +        int nums[3]; | 
|  | 86 | +        state(int xy, int x, int y){ | 
|  | 87 | +            nums[0] = xy; | 
|  | 88 | +            nums[1] = x; | 
|  | 89 | +            nums[2] = y; | 
|  | 90 | +        } | 
|  | 91 | +    }; | 
|  | 92 | +     | 
|  | 93 | +    state pour_water(state cur, int src, int det, int size[]){ | 
|  | 94 | +        state ans = cur; | 
|  | 95 | +        int need_w = size[det] - cur.nums[det]; | 
|  | 96 | +        if(need_w <= cur.nums[src]){ | 
|  | 97 | +            ans.nums[det] += need_w; | 
|  | 98 | +            ans.nums[src] -= need_w; | 
|  | 99 | +        } | 
|  | 100 | +        else{ | 
|  | 101 | +            ans.nums[det] += ans.nums[src]; | 
|  | 102 | +            ans.nums[src] = 0; | 
|  | 103 | +        } | 
|  | 104 | +        return ans; | 
|  | 105 | +    } | 
|  | 106 | +     | 
|  | 107 | +    bool canMeasureWater(int x, int y, int z) { | 
|  | 108 | +        if(x > y) return canMeasureWater(y, x, z);  // | 
|  | 109 | +        if(x == z || y == z) return true; | 
|  | 110 | +        if(x == 0 || x+y < z) return false; | 
|  | 111 | +        if(x+y == z || z == 0) return true; | 
|  | 112 | +        int visited[x+y+1][x+1][y+1]; | 
|  | 113 | +        int water_size[3] = {x+y, x, y}; | 
|  | 114 | +        memset(visited, 0, sizeof(visited)); | 
|  | 115 | +        state cur(x+y, 0, 0); | 
|  | 116 | +        queue<state> q; | 
|  | 117 | +        q.push(cur); | 
|  | 118 | +        int step = 0; | 
|  | 119 | +        while(!q.empty()){ | 
|  | 120 | +            int size = q.size(); | 
|  | 121 | +            while(size){ | 
|  | 122 | +                state temp(0, 0, 0); | 
|  | 123 | +                cur = q.front(); | 
|  | 124 | +                if(cur.nums[1] + cur.nums[2] == z) return true; | 
|  | 125 | +                visited[cur.nums[0]][cur.nums[1]][cur.nums[2]] = 1; | 
|  | 126 | +                q.pop(); | 
|  | 127 | +                if(cur.nums[0] != 0){ | 
|  | 128 | +                    temp = pour_water(cur, 0, 1, water_size); | 
|  | 129 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 130 | +                        q.push(temp); | 
|  | 131 | +                    temp = pour_water(cur, 0, 2, water_size); | 
|  | 132 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 133 | +                        q.push(temp); | 
|  | 134 | +                } | 
|  | 135 | +                if(cur.nums[1] != 0){ | 
|  | 136 | +                    temp = pour_water(cur, 1, 2, water_size); | 
|  | 137 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 138 | +                        q.push(temp); | 
|  | 139 | +                    temp = pour_water(cur, 1, 0, water_size); | 
|  | 140 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 141 | +                        q.push(temp); | 
|  | 142 | +                } | 
|  | 143 | +                if(cur.nums[2] != 0){ | 
|  | 144 | +                    temp = pour_water(cur, 2, 1, water_size); | 
|  | 145 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 146 | +                        q.push(temp); | 
|  | 147 | +                    temp = pour_water(cur, 2, 0, water_size); | 
|  | 148 | +                    if(visited[temp.nums[0]][temp.nums[1]][temp.nums[2]] != 1) | 
|  | 149 | +                        q.push(temp); | 
|  | 150 | +                } | 
|  | 151 | +                size--; | 
|  | 152 | +            } | 
|  | 153 | +            step++; | 
|  | 154 | +        } | 
|  | 155 | +        return false; | 
|  | 156 | +    } | 
|  | 157 | +}; | 
|  | 158 | +``` | 
|  | 159 | + | 
|  | 160 | +## 优秀解答 | 
|  | 161 | + | 
|  | 162 | +>暂缺 | 
0 commit comments