365. 水壶问题
难度: 中等
题目大意:
有两个水壶,容量分别为
jug1Capacity
和jug2Capacity
升。水的供应是无限的。确定是否有可能使用这两个壶准确得到targetCapacity
升。如果可以得到
targetCapacity
升水,最后请用以上水壶中的一或两个来盛放取得的targetCapacity
升水。你可以:
- 装满任意一个水壶
- 清空任意一个水壶
- 从一个水壶向另外一个水壶倒水,直到装满或者倒空
1 <= jug1Capacity, jug2Capacity, targetCapacity <= 10^6
示例 1:
输入: jug1Capacity = 3, jug2Capacity = 5, targetCapacity = 4 输出: true 解释:来自著名的 "Die Hard"
分析
我们可以直接进行暴搜,一共可以进行的操作是
- 将
x
加满 - 将
y
加满 - 将
x
清空 - 将
y
清空 - 将
x
的水倒到y
中 - 将
y
的水倒到x
中
为了防止遍历重复的情况,我们可以用一个数组来存储状态,防止递归层数过深,我们可以使用栈来模拟递归过程
搜索
using PII = pair<int, int>;
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
stack<PII> stk;
stk.emplace(0, 0);
// pair<int, int>使用不了默认的哈希函数,所以要重新写一下哈希函数
auto hash_function = [](const PII& o) { return hash<int>()(o.first) ^ hash<int>()(o.second ); };
// 表示是不是遍历过
unordered_set<PII, decltype(hash_function)> seen(0, hash_function);
while (!stk.empty()) {
if (seen.count(stk.top())) {
stk.pop();
continue;
}
seen.emplace(stk.top());
auto [remain_x, remain_y] = stk.top();
stk.pop();
if (remain_x == z || remain_y == z || remain_x + remain_y == z) {
return true;
}
// 把 X 壶灌满。
stk.emplace(x, remain_y);
// 把 Y 壶灌满。
stk.emplace(remain_x, y);
// 把 X 壶倒空。
stk.emplace(0, remain_y);
// 把 Y 壶倒空。
stk.emplace(remain_x, 0);
// 把 X 壶的水灌进 Y 壶,直至灌满或倒空。
stk.emplace(remain_x - min(remain_x, y - remain_y), remain_y + min(remain_x, y - remain_y));
// 把 Y 壶的水灌进 X 壶,直至灌满或倒空。
stk.emplace(remain_x + min(remain_y, x - remain_x), remain_y - min(remain_y, x - remain_x));
}
return false;
}
};
时间复杂度: O ( x y ) O(xy) O(xy)
分析
裴蜀定理(或贝祖定理)得名于法国数学家艾蒂安·裴蜀,说明了对任何整数a、b
和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b
是整数,且gcd(a,b)=d
,那么对于任意的整数x,y,ax+by
都一定是d
的倍数,特别地,一定存在整数x,y
,使ax+by=d
成立。
所以我们可以知道如果gcd(x,y)
是z
的约数,那么说明肯定是可以的,但是要对x == 0和y == 0
的情况特判一下
裴蜀定理
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
if (x + y < z) {
return false;
}
if (x == 0 || y == 0) {
return z == 0 || x + y == z;
}
return z % gcd(x, y) == 0;
}
};
时间复杂度: O ( 1 ) O(1) O(1)
结束了