喂食仓鼠的最小食物桶数 图解题解
这道题到底在问什么
- 输入
- hamsters = "H..H"
- 输出
- 2
- 输入
- hamsters = ".H.H."
- 输出
- 1 (下标 2 一个桶喂两只)
- 输入
- hamsters = ".HHH."
- 输出
- -1 (中间那只两边都是仓鼠,喂不到)
最优解:一步一步想明白
- 3记牢这套口诀:见仓鼠先看右,右边空位就往右放让一桶喂两只;右边堵住再往左放;左右皆堵就无解。下面从最左边一格一格扫。
- 4先把这条街看清楚。H 是仓鼠,点是空位。这条街上有四只仓鼠,分别蹲在下标 0、2、4、5 的位置。我们要在空位上放桶,把它们全喂到,而且桶要放得最少。
- 5把规则再捋一遍:一只仓鼠只要它紧挨着的左边一格,或者右边一格有桶,就算喂到了。反过来看,一个桶其实能同时照顾它左右两边的仓鼠,这正是我们能少放桶的地方。
- 6用一个指针 i 从最左边开始,从左往右扫。ans 记已经放了几个桶,现在是 0。指针先停在下标 0。
- 7指针停在下标 0,这里是个 H,有一只仓鼠。它必须被喂到,也就是左边或右边得挨着一个桶。按口诀,先看它右边能不能放。
- 8顺带说一句,下标 0 的左边已经是街道尽头,越界了,根本没有空位可放。所以对这只仓鼠来说,右边是唯一的指望。正好也符合我们优先往右放的原则。
- 9看它右边,下标 1 是个点,是空位。太好了,能往右放。这里藏着贪心的小心思:右边的桶不光喂当前这只,还可能顺带喂到再往右的那只仓鼠,所以能往右就优先往右。
- 10就在下标 1 放一个桶,涂成绿色。这样下标 0 的仓鼠右边挨着桶了,喂饱,标成蓝色。ans 加到 1。
- 11关键一步来了。下标 1 的这个桶,右边正好是下标 2 的仓鼠,所以这一个桶把下标 0 和下标 2 两只仓鼠一起喂了。既然下标 2 已经安顿好,指针就多跳一格,越过下标 1 和 2,直接蹦到下标 3,你看指针现在就停在下标 3 上,省得回头重复看。这就是优先往右放换来的好处。
- 12指针已经停在下标 3 了,这里只是回头看一眼下标 2 那只仓鼠,并不是把指针挪回去重新扫它。它的左边就是下标 1 的桶,规则说左邻有桶就算喂到,所以它是被顺带喂饱的,不用再单独为它花一个桶。你看,一个桶服务两只,桶数就这么省下来了。
- 13指针落到下标 3,这里是个点,空位,没有仓鼠要照顾。空位本身不需要我们做任何决定,直接往右走。
- 14走到下标 4,又是一只仓鼠。老规矩,先看它右边能不能放桶。
- 15看右边,下标 5 偏偏也是一只仓鼠,不是空位,桶塞不进去。右边这条路堵住了,标成红色。别急,还有左边可以退一步。
- 16右边堵住,回头看左边,下标 3 是个点,空位。那就往左放,先把下标 4 这只仓鼠喂上要紧。注意这是右边放不下时的退路,不是首选。
- 17在下标 3 放一个桶,涂绿。下标 4 的仓鼠左边挨着桶了,喂饱,标蓝。ans 加到 2。这次因为往左放,右边没有第二只能顺带喂,指针就正常往右走一格。
- 18指针来到下标 5,就是刚才右边那只仓鼠,它还没被喂。继续给它安排,还是先看右边。
- 19它右边是下标 6,一个点,空位。能往右放,那就往右放,依然是优先右边这条原则。
- 20在下标 6 放一个桶,涂绿。下标 5 的仓鼠右边挨着桶了,喂饱,标蓝。ans 加到 3。至此四只仓鼠全部有着落。
- 21指针越过街道最右边,循环结束。回头看,下标 0、2、4、5 四只仓鼠全都标成了蓝色,一个不落全喂饱了。
- 22把全局再扫一眼,四只蓝色的仓鼠都被绿色的桶挨着照顾到了。绿色的桶一共三个,分别在下标 1、3、6。三个桶喂饱四只仓鼠,靠的就是让桶 1 一桶喂了两只。
- 23回放整条思路。从左往右扫,遇到仓鼠优先往右放桶好让一桶喂两只,右边放不下才退而往左放,哪边都放不了就返回 -1。数一数绿色的桶,一共三个,这就是喂饱所有仓鼠的最少桶数,答案是 3。
⚠️ 容易写错的地方
✗ 错:见仓鼠先往左放桶
✓ 对:优先往右放,右边放不下再往左
右桶能顺带喂到右边第二只仓鼠,先往左放会错过这个省桶机会,答案可能偏大
✗ 错:往右放桶后忘了跳过被顺带喂到的仓鼠
✓ 对:右放成功后指针多跳一格,略过右边第二只
代码没把桶写回街道、也不判断左边是否已有桶,全靠指针跳过喂饱的仓鼠;不跳就会把它当成没喂再放一个桶,桶数变多、答案偏大出错,所以跳步是正确性要求
✗ 错:仓鼠两边都是仓鼠时还硬凑一个桶
✓ 对:左右都没空位就返回 -1
桶只能放在空位,两边全是仓鼠时这只永远喂不到,整题无解
✗ 错:下标 0 或末位不判越界
✓ 对:看左邻先确认 i 大于 0,看右邻先确认 i 加 1 在界内
不判越界会读到街道之外,导致下标错误或误判
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def minimumBuckets(self, street: str) -> int:
ans = 0
i, n = 0, len(street)
while i < n:
if street[i] == 'H':
if i + 1 < n and street[i + 1] == '.':
i += 2
ans += 1
elif i and street[i - 1] == '.':
ans += 1
else:
return -1
i += 1
return ansC++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
int minimumBuckets(string street) {
int n = street.size();
int ans = 0;
for (int i = 0; i < n; ++i) {
if (street[i] == 'H') {
if (i + 1 < n && street[i + 1] == '.') {
++ans;
i += 2;
} else if (i && street[i - 1] == '.') {
++ans;
} else {
return -1;
}
}
}
return ans;
}
};Java
import java.util.*;
class Solution {
public int minimumBuckets(String street) {
int n = street.length();
int ans = 0;
for (int i = 0; i < n; ++i) {
if (street.charAt(i) == 'H') {
if (i + 1 < n && street.charAt(i + 1) == '.') {
++ans;
i += 2;
} else if (i > 0 && street.charAt(i - 1) == '.') {
++ans;
} else {
return -1;
}
}
}
return ans;
}
}复杂度
时间
O(n)
n 是街道长度。指针从左到右只扫一遍,每个位置只做常数次判断,往右放桶时还会多跳一格,总步数不超过 n
空间
O(1)
只用了 i 和 ans 两个整型变量,不随街道长度增长,不额外开数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 喂食仓鼠的最小食物桶数 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这题的核心贪心策略是什么,为什么是对的?+
从左往右扫,遇到仓鼠优先往右放桶。因为扫描是从左到右,当前仓鼠左边的局面已经定了,把桶放右边能同时覆盖当前这只和右边紧邻的那只,收益最大;只有右边是仓鼠或越界放不下时,才退而往左放。可以证明这样每一步的局部最优不会损害全局,得到的桶数最少。
为什么往右放桶之后指针要多跳一步?+
往右放桶时,桶落在当前仓鼠的右边一格,会顺带喂到它右边第二格的仓鼠,如果那里有仓鼠的话。这只仓鼠已经喂饱,但代码没有把桶写回街道,也不额外判断左边是否已有桶,全靠指针跳过去把它略过。如果不跳,就会把它当成还没喂的仓鼠再放一个桶,答案就偏大出错了。所以多跳一步是保证正确的必要动作,不是可有可无的优化。
如果允许桶放在仓鼠所在的格子上,题目会怎么变?+
那规则就不一样了,本题明确桶只能放空位。正因为只能放空位,才会出现两边都是仓鼠导致无解的情况;若允许原地放桶,任何仓鼠都能自喂,就不存在 -1 了。审题时要盯紧这条约束。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 喂食仓鼠的最小食物桶数 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。