有两个容量分别为 x升 和 y升 的水壶以及无限多的水。请判断能否通过使用这两个水壶,从而可以得到恰好 z升 的水?如果可以,最后请用以上水壶中的一或两个来盛放取得的 z升 水。
你允许:
装满任意一个水壶
清空任意一个水壶
从一个水壶向另外一个水壶倒水,直到装满或者倒空
示例 1: (From the famous “Die Hard” example)
输入: x = 3, y = 5, z = 4
输出: True
示例 2:
输入: x = 2, y = 6, z = 5
输出: False
由题意可知我们可对水壶做三种操作,每次操作可改变水壶的状态,遍历所有状态看是否有满足 得到恰好Z升的水 这个条件的状态,其实这就是个搜索,我们使用广搜来解决这个问题:
bool canMeasureWater(int x, int y, int z) {
if((x+y) < z)
return false;
int num;
queue<pair<int,int>>q;
map<pair<int,int>,int>m;
pair<int,int>p(0,0);
m[p] = 1;
q.push(p);
while(!q.empty())
{
p = q.front();
q.pop();
if(p.first == z || p.second == z || p.first+p.second == z)
return true;
//向第一个水壶加满水
pair<int,int>p1 = make_pair(x,p.second);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
//将第一个水壶清空
p1 = make_pair(0,p.second);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
//把第一个水壶的水倒到第二个水壶,直到装满或者倒空
num = min(p.first,y-p.second);
p1 = make_pair(p.first - num,p.second + num);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
//装满第二个水壶
p1 = make_pair(p.first,y);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
//清空第二个水壶
p1 = make_pair(p.first,0);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
//把第二个水壶的水倒到第一个水壶,直到装满或者倒空
num = min(p.second,x-p.first);
p1 = make_pair(p.first + num,p.second - num);
if(m[p1] == 0)
{
m[p1]=1;
q.push(p1);
}
}
return false;
}
这个方法复杂度较高,还有一种纯数学的解法(数学牛逼~~):对于两个水壶的操作我们可以看成增加x,减少x,增加y,减少y。
为何不存在其它情况?
由题意我们可以知道两个水壶不会同时存在不满的状态,所以当一个水壶不满时,另一个水壶必定为空或者为满,那我们对这个不满的水壶不论做那种操作所得到的状态都是可以通过增加x,减少x,增加y,减少y得到的( 注意我们此时考虑的是所得水量,水在壶间的转移我们是不关心的)
所以可以通过a次x操作和b次y操作可以得到z,即ax+by=z(其中a和b为整数)
对于例一来说:
1.先把3L的壶装满
2.把3L壶中的水倒向5L壶中
3.再把3L壶装满
4.把3L壶中的水倒入已有3L水的5L壶中(此时3L壶中有1L的水,5L壶已满)
5.把5L壶清空
6.把3L壶中剩余的1L水倒入5L壶中
7.将3L壶装满
此时以满足要求得到了4L的水
若将上面的装水的过程通过增加x,减少x,增加y,减少y的方式来看,则变成了
1. +3
2. +3
3. -5
4. +3
也就是3*3-5=4,(a=3,b=-1)
所以这个数学方法是:为了得到Z L水,我只关心你整体的增量和减量,而不关心你具体的实现方式(即壶间水量的转移)。
总结:若ax+by=z,这个方程有解,则可得到Z升的水。由贝祖定理我们可知,该方程有解的条件是z为x和y最大公约数的倍数。
bool canMeasureWater(int x, int y, int z) {
if(x+y < z)
return false;
else if(x == z || y == z || x+y == z)
return true;
else if(z % gcd(x,y) == 0)
return true;
else return false;
}