不准访问
- 积分
- 546
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2020-11-18
- 最后登录
- 1970-1-1
|
方法一:深度优先搜索/回溯
首先对题目进行建模。观察题目可知,在任意一个时刻,此问题的状态可以由两个数字决定:X 壶中的水量,以及 Y 壶中的水量。
在任意一个时刻,我们可以且仅可以采取以下几种操作:
把 X 壶的水灌进 Y 壶,直至灌满或倒空;
把 Y 壶的水灌进 X 壶,直至灌满或倒空;
把 X 壶灌满;
把 Y 壶灌满;
把 X 壶倒空;
把 Y 壶倒空。 因此,本题可以使用深度优先搜索来解决。搜索中的每一步以 remain_x, remain_y 作为状态,即表示 X 壶和 Y 壶中的水量。在每一步搜索时,我们会依次尝试所有的操作,递归地搜索下去。这可能会导致我们陷入无止境的递归,因此我们还需要使用一个哈希结合(HashSet)存储所有已经搜索过的 remain_x, remain_y 状态,保证每个状态至多只被搜索一次
using PII = pair<int, int>;
class Solution {
public:
bool canMeasureWater(int x, int y, int z) {
stack<PII> stk;
stk.emplace(0, 0);
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(xy)$,状态数最多有 $(x+1)(y+1)$ 种,对每一种状态进行深度优先搜索的时间复杂度为 $O(1)$,因此总时间复杂度为 $O(xy)$。
空间复杂度:$O(xy)$,由于状态数最多有 $(x+1)(y+1)$ 种,哈希集合中最多会有 $(x+1)(y+1)$ 项,因此空间复杂度为 $O(xy)$。 |
|