1 介绍
本博客用来记录灵神力扣题单之二分算法。
2 训练
2.1 二分查找
解题思路:二分查找。
C++代码如下,
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
auto it = lower_bound(nums.begin(), nums.end(), target);
if (it == nums.end() || *it != target) {
return {-1,-1};
}
int idx1 = distance(nums.begin(), it);
it = upper_bound(nums.begin(), nums.end(), target);
int idx2 = distance(nums.begin(), it);
idx2 -= 1;
return {idx1, idx2};
}
};
python3代码如下,
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
idx1 = bisect.bisect_left(nums, target)
if not 0 <= idx1 < len(nums) or nums[idx1] != target:
return [-1,-1]
idx2 = bisect.bisect_right(nums, target)
idx2 -= 1
return [idx1, idx2]
题目2:35. 搜索插入位置
解题思路:二分查找。
C++代码如下,
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
auto iter = lower_bound(nums.begin(), nums.end(), target);
return distance(nums.begin(), iter);
}
};
python3代码如下,
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
return bisect.bisect_left(nums, target)
题目3:704. 二分查找
解题思路:二分算法。
C++代码如下,
class Solution {
public:
int search(vector<int>& nums, int target) {
auto it = lower_bound(nums.begin(), nums.end(), target);
int idx = distance(nums.begin(), it);
if (idx == nums.size() || nums[idx] != target) {
return -1;
}
return idx;
}
};
python3代码如下,
class Solution:
def search(self, nums: List[int], target: int) -> int:
idx = bisect.bisect_left(nums, target)
if idx == len(nums) or nums[idx] != target:
return -1
return idx
解题思路:二分算法。
C++代码如下,
class Solution {
public:
char nextGreatestLetter(vector<char>& letters, char target) {
auto it = upper_bound(letters.begin(), letters.end(), target);
int idx = distance(letters.begin(), it);
if (idx == letters.size()) {
idx = 0;
}
return letters[idx];
}
};
python3代码如下,
class Solution:
def nextGreatestLetter(self, letters: List[str], target: str) -> str:
idx = bisect.bisect_right(letters, target)
if idx == len(letters):
idx = 0
return letters[idx]
解题思路:二分算法。
C++代码如下,
class Solution {
public:
int maximumCount(vector<int>& nums) {
auto it = lower_bound(nums.begin(), nums.end(), 0);
int idx1 = distance(nums.begin(), it);
it = upper_bound(nums.begin(), nums.end(), 0);
int idx2 = distance(nums.begin(), it);
int n = nums.size();
return max(idx1, n-idx2);
}
};
python3代码如下,
class Solution:
def maximumCount(self, nums: List[int]) -> int:
idx1 = bisect.bisect_left(nums, 0)
idx2 = bisect.bisect_right(nums, 0)
return max(idx1, len(nums)-idx2)
题目6:1385. 两个数组间的距离值
解题思路:二分算法。
C++代码如下,
class Solution {
public:
int findTheDistanceValue(vector<int>& arr1, vector<int>& arr2, int d) {
sort(arr1.begin(), arr1.end());
sort(arr2.begin(), arr2.end());
int res = 0;
for (auto x : arr1) {
//所有的元素差都大于d
int t = 110;
auto it = lower_bound(arr2.begin(), arr2.end(), x);
int idx = distance(arr2.begin(), it);
if (0 <= idx && idx < arr2.size()) {
t = min(t, abs(arr2[idx]-x));
}
if (0 <= idx-1 && idx-1 < arr2.size()) {
t = min(t, abs(arr2[idx-1]-x));
}
if (t > d) {
res += 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
arr1.sort()
arr2.sort()
res = 0
n = len(arr2)
for x in arr1:
#所有元素差都大于d
idx = bisect.bisect_left(arr2, x)
t = inf
if 0 <= idx < n:
t = min(t, abs(arr2[idx]-x))
if 0 <= idx-1 < n:
t = min(t, abs(arr2[idx-1]-x))
if t > d:
res += 1
return res
题目7:2300. 咒语和药水的成功对数
解题思路:手写二分算法。
C++代码如下,
class Solution {
public:
vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) {
int n = spells.size();
vector<int> res(n, 0);
int m = potions.size();
sort(potions.begin(), potions.end());
for (int i = 0; i < n; ++i) {
//spells[i] * potions[j] >= success的最小下标
int t = -1;
int left = 0, right = m-1;
while (left <= right) {
int mid = (left + right) / 2;
if ((long long)spells[i] * potions[mid] >= success) {
t = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
if (t != -1) {
res[i] = m - t;
}
}
return res;
}
};
python3代码如下,
class Solution:
def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]:
n = len(spells)
res = [0] * n
potions.sort()
for i, x in enumerate(spells):
#x*potions[i] >= success的个数
left = 0
right = len(potions)-1
ans = -1
#找到x*postions[i] >= success的下标
while left <= right:
mid = (left + right) // 2
if x * potions[mid] >= success:
ans = mid
right = mid - 1
else:
left = mid + 1
if ans != -1:
res[i] = len(potions) - ans
return res
题目8:2389. 和有限的最长子序列
解题思路:二分算法。
C++代码如下,
class Solution {
public:
vector<int> answerQueries(vector<int>& nums, vector<int>& queries) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<int> s(n, 0);
s[0] = nums[0];
for (int i = 1; i < n; ++i) {
s[i] = s[i-1] + nums[i];
}
int m = queries.size();
vector<int> res(m, 0);
for (int i = 0; i < queries.size(); ++i) {
int x = queries[i];
auto it = upper_bound(s.begin(), s.end(), x);
int idx = distance(s.begin(), it);
res[i] = idx;
}
return res;
}
};
python3代码如下,
class Solution:
def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]:
nums.sort()
n = len(nums)
s = [0] * n
s[0] = nums[0]
for i in range(1,n):
s[i] = s[i-1] + nums[i]
m = len(queries)
res = [0] * m
for i, x in enumerate(queries):
idx = bisect.bisect_right(s, x)
res[i] = idx
return res
解题思路:模拟+二分。
C++代码如下,
class Solution {
public:
vector<int> numSmallerByFrequency(vector<string>& queries, vector<string>& words) {
function<int(string)> f =[&] (string s) -> int {
map<char, int> cnt;
char t = s[0];
for (auto c : s) {
cnt[c] += 1;
t = min(t, c);
}
return cnt[t];
};
vector<int> a;
for (auto s : queries) {
a.emplace_back(f(s));
}
vector<int> b;
for (auto s : words) {
b.emplace_back(f(s));
}
sort(b.begin(), b.end());
vector<int> res;
for (auto x : a) {
auto it = upper_bound(b.begin(), b.end(), x);
int idx = distance(it, b.end());
res.emplace_back(idx);
}
return res;
}
};
python3代码如下,
class Solution:
def numSmallerByFrequency(self, queries: List[str], words: List[str]) -> List[int]:
def f(s: str) -> int:
cnt = collections.Counter(s)
s = sorted(s)
return cnt[s[0]]
a = []
for s in queries:
a.append(f(s))
b = []
for s in words:
b.append(f(s))
b.sort()
res = [0] * len(a)
for i,x in enumerate(a):
idx = bisect.bisect_right(b, x)
res[i] = len(b) - idx
return res
题目10:2080. 区间内查询数字的频率
解题思路:模拟+二分。对于C++的unordered_map<int, vector<int>> idx
而言,尽量使用idx.find(value)
,避免使用idx[value]
或idx.count(value)
。
C++代码如下,
class RangeFreqQuery {
private:
unordered_map<int, vector<int>> idx;
public:
RangeFreqQuery(vector<int>& arr) {
idx.clear();
for (int i = 0; i < arr.size(); ++i) {
idx[arr[i]].emplace_back(i);
}
return;
}
int query(int left, int right, int value) {
int res = -1;
if (idx.find(value) == idx.end()) res = 0;
else {
const vector<int> &a = idx[value];
auto it = lower_bound(a.begin(), a.end(), left);
int idx1 = distance(a.begin(), it);
it = upper_bound(a.begin(), a.end(), right);
int idx2 = distance(a.begin(), it);
res = idx2-idx1;
}
return res;
}
};
/**
* Your RangeFreqQuery object will be instantiated and called as such:
* RangeFreqQuery* obj = new RangeFreqQuery(arr);
* int param_1 = obj->query(left,right,value);
*/
python3代码如下,
class RangeFreqQuery:
def __init__(self, arr: List[int]):
self.idx = collections.defaultdict(list)
for i,x in enumerate(arr):
self.idx[x].append(i)
return
def query(self, left: int, right: int, value: int) -> int:
if value not in self.idx:
return 0
else:
a = self.idx[value]
idx1 = bisect.bisect_left(a, left)
idx2 = bisect.bisect_right(a, right)
return idx2-idx1
# Your RangeFreqQuery object will be instantiated and called as such:
# obj = RangeFreqQuery(arr)
# param_1 = obj.query(left,right,value)
题目11:2563. 统计公平数对的数目
解题思路:二分+双指针。或者仅二分。bisect.bisect_left(nums, lower-x, i+1, n)
表示在[i+1,n)
之间找到第一个>=lower-x
的元素的下标,等价于C++中lower_bound(nums.begin()+i+1, nums.end(), lower-x)
。
C++代码如下,
class Solution {
public:
long long countFairPairs(vector<int>& nums, int lower, int upper) {
sort(nums.begin(), nums.end());
long long res = 0;
int n = nums.size();
for (int i = 0; i < nums.size(); ++i) {
int x = nums[i];
auto it = lower_bound(nums.begin()+i+1, nums.end(), lower-x);
int idx1 = distance(nums.begin(), it);
it = upper_bound(nums.begin()+i+1, nums.end(), upper-x);
int idx2 = distance(nums.begin(), it);
res += idx2 - idx1;
}
return res;
}
};
python3代码如下,
#仅二分
class Solution:
def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int:
nums.sort()
res = 0
n = len(nums)
for i in range(n):
x = nums[i]
idx1 = bisect.bisect_left(nums, lower-x, i+1, n)
idx2 = bisect.bisect_right(nums, upper-x, i+1, n)
res += idx2-idx1
return res
#二分+双指针,注意分类讨论
class Solution:
def countFairPairs(self, nums: List[int], lower: int, upper: int) -> int:
nums.sort()
n = len(nums)
left = 0
right = 0
res = 0
for i,x in enumerate(nums):
if i == 0:
left = bisect.bisect_left(nums[i+1:], lower-x)
left += i + 1
right = bisect.bisect_right(nums[i+1:], upper-x)
right += i + 1
res += right - left
else:
if left == n:
if nums[-1] < lower-x:
continue #跳过
else:
left -= 1
if left > i and nums[left] >= lower-x:
while left > i and nums[left] >= lower-x:
left -= 1
left += 1
left = max(left, i+1)
if right == n:
if nums[-1] <= upper-x:
res += right - left
continue #跳过
else:
right -= 1
if right >= left and nums[right] > upper-x:
while right >= left and nums[right] > upper-x:
right -= 1
right += 1
right = max(right, i+1)
res += right - left
if i + 1 < n and nums[i] + nums[i+1] > upper:
break
return res
题目12:2856. 删除数对后的最小数组长度
解题思路:哈希表。
C++代码如下,
class Solution {
public:
int minLengthAfterRemovals(vector<int>& nums) {
map<int,int> cnt;
for (auto x : nums) cnt[x]++;
vector<int> a;
for (auto [key, value] : cnt) a.emplace_back(value);
sort(a.begin(), a.end());
long long s = accumulate(a.begin(), a.end(), 0);
long long maxv = a.back();
long long left = s - maxv;
if (maxv == left) return 0;
else if (maxv < left) return s % 2;
else return maxv-left;
}
};
python3代码如下,
class Solution:
def minLengthAfterRemovals(self, nums: List[int]) -> int:
cnt = collections.Counter(nums)
a = list(cnt.values())
a.sort()
s = sum(a)
maxv = a[-1]
left = s - a[-1]
if maxv == left:
return 0
elif maxv < left:
return s % 2
else:
return maxv - left
题目13:981. 基于时间的键值存储
解题思路:二分。
C++代码如下,
class TimeMap {
private:
unordered_map<string, vector<int>> a;
unordered_map<int, string> b;
public:
TimeMap() {
a.clear();
b.clear();
}
void set(string key, string value, int timestamp) {
a[key].emplace_back(timestamp);
b[timestamp] = value;
}
string get(string key, int timestamp) {
if (a.find(key) == a.end()) {
return "";
} else {
const vector<int> &nums = a[key];
auto it = upper_bound(nums.begin(), nums.end(), timestamp);
int idx = distance(nums.begin(), it);
idx -= 1;
if (idx < 0) {
return "";
} else {
return b[nums[idx]];
}
}
}
};
/**
* Your TimeMap object will be instantiated and called as such:
* TimeMap* obj = new TimeMap();
* obj->set(key,value,timestamp);
* string param_2 = obj->get(key,timestamp);
*/
python3代码如下,
class TimeMap:
def __init__(self):
self.a = collections.defaultdict(list)
self.b = collections.defaultdict(str)
def set(self, key: str, value: str, timestamp: int) -> None:
self.a[key].append(timestamp)
self.b[timestamp] = value
def get(self, key: str, timestamp: int) -> str:
if key not in self.a:
return ""
else:
nums = self.a[key]
idx = bisect.bisect_right(nums, timestamp)
idx -= 1
if idx < 0:
return ""
else:
return self.b[nums[idx]]
# Your TimeMap object will be instantiated and called as such:
# obj = TimeMap()
# obj.set(key,value,timestamp)
# param_2 = obj.get(key,timestamp)
题目14:1146. 快照数组
解题思路:存储set记录。
C++代码如下,
class SnapshotArray {
private:
unordered_map<int, vector<pair<int,int>>> a;
int snap_id = 0;
public:
SnapshotArray(int length) {
a.clear();
snap_id = 0;
}
void set(int index, int val) {
a[index].emplace_back(make_pair(snap_id,val));
}
int snap() {
snap_id += 1;
return snap_id-1;
}
int get(int index, int snap_id) {
const vector<pair<int,int>> &nums = a[index];
pair<int,int> t = make_pair(snap_id,int(1e9+10));
auto it = upper_bound(nums.begin(), nums.end(), t);
int idx = distance(nums.begin(), it) - 1;
return idx < 0 ? 0 : nums[idx].second;
}
};
/**
* Your SnapshotArray object will be instantiated and called as such:
* SnapshotArray* obj = new SnapshotArray(length);
* obj->set(index,val);
* int param_2 = obj->snap();
* int param_3 = obj->get(index,snap_id);
*/
python3代码如下,
class SnapshotArray:
def __init__(self, length: int):
self.a = collections.defaultdict(list)
self.snapid = 0
def set(self, index: int, val: int) -> None:
self.a[index].append([self.snapid,val])
def snap(self) -> int:
self.snapid += 1
return self.snapid-1
def get(self, index: int, snap_id: int) -> int:
idx = bisect.bisect_right(self.a[index], [snap_id,1e10]) - 1
return self.a[index][idx][1] if idx >= 0 else 0
# Your SnapshotArray object will be instantiated and called as such:
# obj = SnapshotArray(length)
# obj.set(index,val)
# param_2 = obj.snap()
# param_3 = obj.get(index,snap_id)
题目15:1818. 绝对差值和
解题思路:二分。注意最后再取模。
C++代码如下,
class Solution {
public:
int minAbsoluteSumDiff(vector<int>& nums1, vector<int>& nums2) {
vector<pair<int,int>> diff;
int n = nums1.size();
long long sumd = 0;
for (int i = 0; i < n; ++i) {
int t = abs(nums1[i]-nums2[i]);
diff.emplace_back(make_pair(t,i));
sumd += t;
}
//cout << "sumd = " << sumd << endl;
sort(diff.begin(),diff.end());
reverse(diff.begin(), diff.end());
sort(nums1.begin(), nums1.end());
const int mod = 1e9 + 7;
long long res = sumd;
for (int i = 0; i < n; ++i) {
int curr_d = diff[i].first;
int curr_i = diff[i].second;
int target = nums2[curr_i];
auto it = lower_bound(nums1.begin(), nums1.end(), target);
int idx1 = distance(nums1.begin(), it);
int idx2 = idx1 - 1;
int new_d = 1e5+10;
if (0 <= idx1 && idx1 < n) {
new_d = min(new_d, abs(nums1[idx1]-target));
}
if (0 <= idx2 && idx2 < n) {
new_d = min(new_d, abs(nums1[idx2]-target));
}
//cout << "i = " << i << ", curr_i = " << i << ", curr_d = " << curr_d << ", new_d = " << new_d << endl;
long long curr_res = sumd - curr_d + new_d;
res = min(res, curr_res);
}
int ans = res % mod;
return ans;
}
};
python3代码如下,
class Solution:
def minAbsoluteSumDiff(self, nums1: List[int], nums2: List[int]) -> int:
n = len(nums1)
diff = [0] * n
t = []
sumd = 0
for i in range(n):
diff[i] = abs(nums1[i]-nums2[i])
t.append([diff[i],i])
sumd += diff[i]
t.sort(reverse=True)
a = copy.deepcopy(nums1)
a.sort()
res = sumd
for i in range(n):
curr_d, curr_i = t[i]
target = nums2[curr_i]
idx1 = bisect.bisect_left(a, target)
idx2 = idx1 - 1
new_d = 1e6
if 0 <= idx1 < n:
new_d = min(new_d, abs(target-a[idx1]))
if 0 <= idx2 < n:
new_d = min(new_d, abs(target-a[idx2]))
res = min(res, sumd-curr_d+new_d)
return res % int(1e9+7)
题目16:911. 在线选举
解题思路:python3,有序列表+二分。C++,有序集合+二分。
C++代码如下,
class TopVotedCandidate {
private:
vector<vector<int>> res;
public:
TopVotedCandidate(vector<int>& persons, vector<int>& times) {
//处理出每个时刻的获胜者
int n = persons.size();
unordered_map<int,vector<int>> cnt;
set<vector<int>> values;
vector<vector<int>> res;
for (int i = 0; i < n; ++i) {
int p = persons[i];
int t = times[i];
if (cnt.find(p) == cnt.end()) {
cnt[p] = {1,t,p};
values.insert(cnt[p]);
} else {
vector<int> prev_value = cnt[p];
cnt[p] = {prev_value[0]+1,t,p};
values.erase(prev_value);
values.insert(cnt[p]);
}
vector<int> target = *values.rbegin();
res.push_back({target[1],target[2]});
}
this->res.clear();
this->res = res;
return;
}
int q(int t) {
vector<int> target = {t, 5006};
auto it = upper_bound(res.begin(), res.end(), target);
int idx = distance(res.begin(), it) - 1;
if (idx < 0) {
cout << "Error!" << endl;
return -1;
}
return res[idx][1];
}
};
/**
* Your TopVotedCandidate object will be instantiated and called as such:
* TopVotedCandidate* obj = new TopVotedCandidate(persons, times);
* int param_1 = obj->q(t);
*/
python3代码如下,
from sortedcontainers import SortedList
class TopVotedCandidate:
def __init__(self, persons: List[int], times: List[int]):
#计算出每个时刻的获胜者
n = len(persons)
cnt = defaultdict(list)
res = []
sa = SortedList()
for i in range(n):
t = times[i]
p = persons[i]
prev_value = cnt[p]
if len(prev_value) == 0:
cnt[p] = [1,t,p]
sa.add(cnt[p])
else:
cnt[p] = [prev_value[0]+1,t,p]
sa.remove(prev_value)
sa.add(cnt[p])
res.append(sa[-1][1:])
self.res = res
print(res)
return
def q(self, t: int) -> int:
res = self.res
idx = bisect_right(res, [t,5007])
idx -= 1
if idx < 0:
print("Error!")
return -1
return res[idx][1]
# Your TopVotedCandidate object will be instantiated and called as such:
# obj = TopVotedCandidate(persons, times)
# param_1 = obj.q(t)
题目17:LCP 08. 剧情触发时间
解题思路:二分。
C++代码如下,
class Solution {
public:
vector<int> getTriggerTime(vector<vector<int>>& increase, vector<vector<int>>& requirements) {
int n = increase.size();
vector<int> a(n+1,0), b(n+1,0), c(n+1,0);
for (int i = 1; i <= n; ++i) {
a[i] = a[i-1] + increase[i-1][0];
b[i] = b[i-1] + increase[i-1][1];
c[i] = c[i-1] + increase[i-1][2];
}
int m = requirements.size();
vector<int> res(m, -1);
for (int i = 0; i < m; ++i) {
vector<int> t = requirements[i];
auto it = lower_bound(a.begin(), a.end(), t[0]);
int idx1 = distance(a.begin(), it);
it = lower_bound(b.begin(), b.end(), t[1]);
int idx2 = distance(b.begin(), it);
it = lower_bound(c.begin(), c.end(), t[2]);
int idx3 = distance(c.begin(), it);
int idx = max(max(idx1,idx2),idx3);
if (idx < n+1) {
res[i] = idx;
}
}
return res;
}
};
python3代码如下,
class Solution:
def getTriggerTime(self, increase: List[List[int]], requirements: List[List[int]]) -> List[int]:
n = len(increase)
a = [0] * (n+1)
b = [0] * (n+1)
c = [0] * (n+1)
for i in range(1,n+1):
a[i] = a[i-1] + increase[i-1][0]
b[i] = b[i-1] + increase[i-1][1]
c[i] = c[i-1] + increase[i-1][2]
#print(f"a = {a}, b = {b}, c = {c}.")
m = len(requirements)
res = [-1] * m
for i in range(m):
x, y, z = requirements[i]
idx1 = bisect_left(a, x)
idx2 = bisect_left(b, y)
idx3 = bisect_left(c, z)
idx = max([idx1, idx2, idx3])
#print(f"i = {i}, x = {x}, y = {y}, z = {z}, idx = {idx}.")
if idx < n+1:
res[i] = idx
return res
2.2 二分答案:求最小
题目18:1283. 使结果不超过阈值的最小除数
解题思路:自己实现二分。
C++代码如下,
class Solution {
public:
int smallestDivisor(vector<int>& nums, int threshold) {
function<bool(int)> check =[&] (int x) -> bool {
int res = 0;
for (auto num : nums) {
res += (num + x - 1) / x;
}
return res <= threshold;
};
int left = 1;
int right = 1e6 + 10;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def smallestDivisor(self, nums: List[int], threshold: int) -> int:
def check(x: int) -> bool:
nonlocal nums, threshold
s = 0
for num in nums:
s += (num + x - 1) // x #上取整
return s <= threshold
#题目保证有解
left = 1
right = max(nums) + 10
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目19:2187. 完成旅途的最少时间
解题思路:二分。C++版本注意使用long long
。
C++代码如下,
class Solution {
public:
long long minimumTime(vector<int>& time, int totalTrips) {
function<bool(long long)> check =[&] (long long x) -> bool {
long long res = 0;
for (int num : time) {
res += x / num;
if (res >= totalTrips) return true;
}
return false;
};
long long left = 1;
long long right = 1e18;
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimumTime(self, time: List[int], totalTrips: int) -> int:
def check(x: int) -> bool:
nonlocal time, totalTrips
res = 0
for num in time:
res += x // num
return res >= totalTrips
left = 1
right = int(1e20)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目20:1870. 准时到达的列车最小时速
解题思路:二分。注意读题,生成的测试用例保证答案不超过 1e7
。
C++代码如下,
class Solution {
public:
int minSpeedOnTime(vector<int>& dist, double hour) {
function<bool(int)> check =[&] (int x) -> bool {
double ans = 0.0;
int n = dist.size();
for (int i = 0; i < n-1; ++i) {
ans += (dist[i] + x - 1) / x;
}
ans += (double)dist[n-1] / x;
return ans <= hour;
};
int left = 1;
int right = 1e8;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minSpeedOnTime(self, dist: List[int], hour: float) -> int:
def check(x: int) -> bool:
nonlocal dist, hour
res = 0.0
n = len(dist)
for i in range(n-1):
res += (dist[i] + x - 1) // x
res += dist[-1] / x
return res <= hour
n = len(dist)
if hour < float(n-1): #至少需要n-1个小时
return -1
left = 1
right = int(1e8)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目21:1011. 在 D 天内送达包裹的能力
解题思路:二分+模拟。
C++代码如下,
class Solution {
public:
int shipWithinDays(vector<int>& weights, int days) {
function<bool(int)> check =[&] (int val) -> bool{
//运载能力为val
int ans = 0;
int cur = 0;
for (int x : weights) {
cur += x;
if (cur > val) {
ans += 1;
cur = x;
}
}
if (cur > 0) {
ans += 1;
}
return ans <= days;
};
int left = ranges::max(weights);
int right = 1e8; //运载能力的最大值,一艘船给所有货物运完
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def shipWithinDays(self, weights: List[int], days: int) -> int:
#每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹
def check(val: int) -> bool:
nonlocal weights, days
ans = 0
cur = 0
for x in weights:
cur += x
if cur > val:
ans += 1
cur = x
if cur > 0:
ans += 1
return ans <= days
left = max(weights)
right = int(1e8)
res = -1
while left <= right:
mid = (left + right) // 2
#print(f"mid = {mid}, check(mid) = {check(mid)}.")
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目22:875. 爱吃香蕉的珂珂
解题思路:二分。
C++代码如下,
class Solution {
public:
int minEatingSpeed(vector<int>& piles, int h) {
function<bool(long long)> check =[&] (long long v) -> bool {
long long tot = 0;
for (auto pile : piles) {
tot += (pile+v-1) / v;
}
return tot <= h;
};
long long left = 1;
long long right = 0;
for (auto x : piles) right += x;
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minEatingSpeed(self, piles: List[int], h: int) -> int:
def check(v: int) -> bool:
nonlocal piles, h
tot = 0
for x in piles:
tot += (x+v-1) // v
return tot <= h
left = 1
right = sum(piles)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目23:475. 供暖器
解题思路:二分。
C++代码如下,
class Solution {
public:
int findRadius(vector<int>& houses, vector<int>& heaters) {
sort(heaters.begin(), heaters.end());
int res = 0;
for (auto house : houses) {
//找到离house最近的加热器
auto it = lower_bound(heaters.begin(), heaters.end(), house);
int idx1 = distance(heaters.begin(), it);
int idx2 = idx1 - 1;
int mind = int(1e9);
if (0 <= idx1 && idx1 < heaters.size()) {
mind = min(mind, abs(house-heaters[idx1]));
}
if (0 <= idx2 && idx2 < heaters.size()) {
mind = min(mind, abs(house-heaters[idx2]));
}
res = max(res, mind);
}
return res;
}
};
python3代码如下,
class Solution:
def findRadius(self, houses: List[int], heaters: List[int]) -> int:
houses.sort()
heaters.sort()
res = 0
for house in houses:
#离house最近的热水器
idx1 = bisect_left(heaters, house)
idx2 = idx1 - 1
mind = int(1e9)
if 0 <= idx1 < len(heaters):
mind = min(mind, abs(heaters[idx1]-house))
if 0 <= idx2 < len(heaters):
mind = min(mind, abs(heaters[idx2]-house))
res = max(res, mind)
return res
题目24:2594. 修车的最少时间
解题思路:二分。
C++代码如下,
class Solution {
public:
long long repairCars(vector<int>& ranks, int cars) {
function<bool(long long)> check =[&] (long long t) -> bool {
//在时间t内能否完成工作
long long tot = 0;
for (int rank : ranks) {
long long val = (double)t / rank;
double x = sqrt(val);
tot += (int)x;
if (tot >= cars) return true;
}
return false;
};
long long res = -1;
long long left = 1;
long long right = 1e14 + 10;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def repairCars(self, ranks: List[int], cars: int) -> int:
def check(t: int) -> bool:
nonlocal ranks, cars
#在时间t内能否完成工作
tot = 0
for rank in ranks:
x = t / rank
cnt = math.sqrt(x)
tot += int(cnt)
return tot >= cars
left = 1
right = int(1e14+10)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目25:1482. 制作 m 束花所需的最少天数
解题思路:二分。
C++代码如下,
class Solution {
public:
int minDays(vector<int>& bloomDay, int m, int k) {
function<bool(int)> check =[&] (int day) -> bool {
//判断day是否满足
int n = bloomDay.size();
vector<bool> st(n, false);
for (int i = 0; i < n; ++i) {
if (bloomDay[i] <= day) {
st[i] = true;
}
}
int ans = 0;
int left = 0;
while (left < n) {
if (st[left]) {
int cnt = 0;
while (left < n && st[left]) {
cnt += 1;
left += 1;
}
ans += cnt / k;
} else {
//
}
left += 1;
}
return ans >= m;
};
int left = ranges::min(bloomDay);
int right = ranges::max(bloomDay);
int n = bloomDay.size();
if (n < (long long)m * k) { //特判花不足的情况
return -1;
}
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
def check(day: int)->bool:
nonlocal bloomDay, m, k
#确定day天能否制作m个花团
n = len(bloomDay)
st = [False] * n
for i in range(n):
if bloomDay[i] <= day:
st[i] = True
left = 0
ans = 0
while left < n:
if st[left]:
cnt = 0
while left < n and st[left]:
cnt += 1
left += 1
ans += cnt // k
else:
pass
left += 1
return ans >= m
left = min(bloomDay)
right = max(bloomDay)
n = len(bloomDay)
if n < m * k: #不满足制作要求
return -1
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目26:3048. 标记所有下标的最早秒数 I
解题思路:二分+懒标记。
C++代码如下,
class Solution {
public:
int earliestSecondToMarkIndices(vector<int>& nums, vector<int>& changeIndices) {
function<bool(long long)> check =[&] (long long mid) -> bool {
long long n = nums.size();
long long m = mid;
vector<int> map_i_lastj(n, -1); //课程i的最晚的考试时间
for (int j = 0; j < m; ++j) {
int i = changeIndices[j];
i -= 1;
map_i_lastj[i] = j;
}
for (int i = 0; i < n; ++i) { //特判没有考试时间
if (map_i_lastj[i] == -1) {
return false;
}
}
int cnt = 0; //懒标记
for (int j = 0; j < m; ++j) {
int i = changeIndices[j] - 1;
int lastj = map_i_lastj[i];
if (j == lastj) {
if (cnt < nums[i]) { //没有复习时间
return false;
} else {
cnt -= nums[i];
}
} else {
cnt += 1;
}
}
return true;
};
int n = nums.size();
int m = changeIndices.size();
long long left = n;
for (auto x : nums) left += x;
long long right = m;
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def earliestSecondToMarkIndices(self, nums: List[int], changeIndices: List[int]) -> int:
def check(mid: int) -> bool:
nonlocal nums, changeIndices
n = len(nums)
m = mid
map_i_lastj = [-1] * n #第i个物品,最晚的拾取时刻lastj
for j in range(m):
i = changeIndices[j]
map_i_lastj[i-1] = j
#判断mid是否能满足条件
for i in range(0,n):
lastj = map_i_lastj[i]
if lastj == -1:
return False
cnt = 0
for j in range(m):
i = changeIndices[j]
i -= 1
lastj = map_i_lastj[i]
if j == lastj:
if nums[i] > cnt:
return False
cnt -= nums[i]
else:
cnt += 1
return True
n = len(nums)
m = len(changeIndices)
left = n + sum(nums)
right = m
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目27:3049. 标记所有下标的最早秒数 II
解题思路:由题意可知,
(1)对于一门课程,它要么快速复习,要么慢速复习。不存在“慢速复习一半,然后再快速复习”的情况。
(2)对于一门课程,只要选择了快速复习,那么它一定在最左侧那天进行快速复习。比如假设第1门课程,它可以在第1天进行快速复习、可以在第3天进行快速复习、可以在第5天进行快速复习,那么它一定是在第1天进行快速复习。因为这样后面有充足的时间安排考试。
(3)对于一门课程,如果遍历到它的快速复习日,但后面又没有天数用来考试,那么可以尝试反悔一次快速复习。
(4)如果一门课程的所需的复习天数<= 1
,那么它一定是慢速复习。
C++代码如下,
class Solution {
public:
int earliestSecondToMarkIndices(vector<int>& nums, vector<int>& changeIndices) {
long long total = 0;
int n = nums.size();
int m = changeIndices.size();
for (auto x : nums) total += x;
total += n;
vector<int> map_i_firstj(n, -1); //第i门课程在第j天进行快速复习
for (int j = m-1; j >= 0; --j) {
int i = changeIndices[j]-1;
map_i_firstj[i] = j;
}
function<bool(int)> check =[&] (int mid) -> bool {
int m = mid;
int n = nums.size();
priority_queue<int,vector<int>,greater<int>> hp; //小根堆
int cnt = 0; //当前天之后可用来考试的天数。从快速复习角度考虑。
long long slow = total; //复习和考试所需的总天数。从慢速复习角度考虑。
for (int j = m-1; j >= 0; --j) {
int i = changeIndices[j] - 1;
if (nums[i] <= 1) {
cnt += 1;
continue;
}
if (j == map_i_firstj[i]) {
if (cnt > 0) {
//第j天快速复习课程i
cnt -= 1; //从后面抽出一天用来进行课程i的考试
slow -= nums[i] + 1;
hp.push(nums[i]);
} else {
if (hp.size() > 0 && nums[i] > hp.top()) {
//撤销一次快速复习及其考试
int x = hp.top();
hp.pop();
cnt += 2;
slow += x + 1;
//第j天快速复习课程i
cnt -= 1; //从后面抽出一天用来进行课程i的考试
slow -= nums[i] + 1;
hp.push(nums[i]);
} else {
cnt += 1; //第j天不能进行快速复习,保存起来,用来考试
}
}
} else {
cnt += 1; //第j天不能进行快速复习,保存起来,用来考试
}
}
return cnt >= slow;
};
int left = n;
int right = m;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
import heapq
class Solution:
def earliestSecondToMarkIndices(self, nums: List[int], changeIndices: List[int]) -> int:
def check(mid: int) -> bool:
nonlocal nums, changeIndices, map_i_firstj
n = len(nums)
m = mid
cnt = 0 #右侧还剩余可以用来考试的天数 #从快速复习角度考虑
slow = n + sum(nums) #需要用来复习和考试的天数 #从慢速复习角度考虑
hp = [] #记录快速复习的课程
for j in range(m-1,-1,-1):
i = changeIndices[j]-1
if nums[i] <= 1:
cnt += 1 #没有必要对课程i进行快速复习。先存着,用来进行考试。
continue
if j == map_i_firstj[i]:
if cnt > 0:
#第j天快速复习第i门课程
cnt -= 1 #从右侧抽出一天用来第i门课程的考试
slow -= nums[i] + 1
heapq.heappush(hp, nums[i])
else:
if len(hp) > 0 and nums[i] > hp[0]:
#撤销之前的某门课程的快速复习和考试
x = heapq.heappop(hp)
cnt += 2
slow += x + 1
#第j天快速复习第i门课程
cnt -= 1
slow -= nums[i] + 1
heapq.heappush(hp, nums[i])
else:
cnt += 1 #第j天无法进行快速复习,先存着用来考试
else:
cnt += 1 #第j天无法进行快速复习,先存着用来考试
return cnt >= slow
n = len(nums)
m = len(changeIndices)
map_i_firstj = [-1] * n #第i门课程在第j天进行快速复习
for j in range(m-1,-1,-1):
i = changeIndices[j]-1
map_i_firstj[i] = j
left = n
right = m
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
2.3 二分答案:求最大
题目28:275. H 指数 II
解题思路:手写二分。
C++代码如下,
class Solution {
public:
int hIndex(vector<int>& citations) {
int n = citations.size();
int left = 0;
int right = n;
int res = -1;
function<bool(int)> check =[&] (int mid) -> bool {
auto it = lower_bound(citations.begin(), citations.end(), mid);
int ans = distance(it, citations.end());
return ans >= mid;
};
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def hIndex(self, citations: List[int]) -> int:
def check(mid: int) -> bool:
nonlocal citations
#至少有mid篇论文被引用了至少mid次
idx = bisect.bisect_left(citations, mid)
ans = len(citations) - idx #引用次数大于等于mid的论文数
return ans >= mid
n = len(citations)
res = -1
left = 0
right = n
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目29:2226. 每个小孩最多能分到多少糖果
解题思路:二分。
C++代码如下,
class Solution {
public:
int maximumCandies(vector<int>& candies, long long k) {
function<bool(int)> check =[&] (int mid) -> bool {
if (mid == 0) { //特判
return true;
}
long long ans = 0;
for (auto x : candies) {
ans += x / mid;
}
return ans >= k;
};
int n = candies.size();
int right = ranges::max(candies);
int left = 0;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maximumCandies(self, candies: List[int], k: int) -> int:
def check(mid: int) -> bool:
nonlocal candies, k
#每个小孩能否分到mid个糖果
if mid == 0: #特判
return True
ans = 0
for x in candies:
ans += x // mid
return ans >= k
n = len(candies)
right = max(candies)
left = 0
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目30:2982. 找出出现至少三次的最长特殊子字符串 II
解题思路:python3
二分。C++
二分会超时,使用直接法。
C++代码如下,
class Solution {
public:
int maximumLength(string s) {
int n = s.size();
int i = 0;
vector<int> a[26]; //26个vector<int>b
while (i < n) {
int j = i;
while (j + 1 < n && s[j] == s[j+1]) {
j += 1;
}
int t = j - i + 1;
a[s[i]-'a'].emplace_back(t);
i = j + 1; //更新i
}
int res = 0;
for (auto b : a) {
if (b.empty()) { //特判
continue;
}
ranges::sort(b, greater()); //降序排列
b.emplace_back(0);
b.emplace_back(0);
res = max({res, b[0]-2, min(b[0]-1, b[1]), b[2]});
}
if (res == 0) {
res = -1;
}
return res;
}
};
python3代码如下,
class Solution:
def maximumLength(self, s: str) -> int:
def check(mid: int) -> bool:
#s中存在特殊子字符串的长度为mid,并且出现了至少3次
for i in range(26):
c = chr(ord('a')+i)
t = c * mid
#验证t是否出现了至少3次
idx = s.find(t)
cnt = 0
while idx != -1:
cnt += 1
if cnt >= 3:
return True
idx = s.find(t, idx + 1)
return False
n = len(s)
left = 0
right = n
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
if res == 0:
res = -1
return res
题目31:2576. 求出最多标记下标
解题思路:二分。
C++代码如下,
class Solution {
public:
int maxNumOfMarkedIndices(vector<int>& nums) {
int n = nums.size();
int start = n / 2;
int res = 0;
sort(nums.begin(), nums.end());
for (int i = 0; i < n/2; ++i) {
int target = nums[i] * 2;
auto it = lower_bound(nums.begin() + start, nums.end(), target);
int idx = distance(nums.begin(), it);
if (idx == n) {
break;
} else {
res += 1;
start = idx + 1;
}
}
return res * 2;
}
};
python3代码如下,
class Solution:
def maxNumOfMarkedIndices(self, nums: List[int]) -> int:
n = len(nums)
start = n // 2
res = 0
nums.sort()
for i in range(n//2):
target = nums[i] * 2
idx = bisect.bisect_left(nums, target, start, n)
if idx == n:
break
else:
res += 1
start = idx + 1
return res * 2
题目32:1898. 可移除字符的最大数目
解题思路:二分。
C++代码如下,
class Solution {
public:
int maximumRemovals(string s, string p, vector<int>& removable) {
function<bool(int)> check =[&] (int mid) -> bool {
int n = s.size();
vector<int> st(n, false);
for (int i = 0; i < mid; ++i) {
st[removable[i]] = true;
}
string new_s = "";
for (int i = 0; i < s.size(); ++i) {
if (st[i] == false) {
new_s += s[i];
}
}
n = new_s.size();
int m = p.size();
int i = 0, j = 0;
while (i < n && j < m) {
if (new_s[i] == p[j]) {
j += 1;
}
i += 1;
}
return j == m;
};
int left = 1;
int right = removable.size();
int res = 0;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int:
def f(s: str, p: str) -> bool:
#p是s的子串
n = len(s)
m = len(p)
i, j = 0, 0
while i < n and j < m:
if s[i] == p[j]:
j += 1
i += 1
return j == m
def check(mid: int) -> bool:
nonlocal s, p, removable
#移除[0,mid)之后,p仍旧是s的子串
#有些像最长公共子数组
n = len(s)
st = [False] * n
for i in range(mid):
st[removable[i]] = True
new_s = ""
for i in range(n):
if not st[i]:
new_s += s[i]
return f(new_s, p)
left = 1
right = len(removable)
res = 0
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目33:1802. 有界数组中指定下标处的最大值
解题思路:
(1)贪心的思想,从nums[index]
开始,往左和往右,下标每相差1,元素值就减少1,直到到达数组边界,或者减少到仅为1后保持为1不变。
(2)根据这个思路,一旦nums[index]
确定一下,数组的和的最小值也就确定下来了。
(3)C++
要特别注意int
超出范围。
C++代码如下,
class Solution {
public:
int maxValue(int n, int index, int maxSum) {
function<bool(int)> check =[&] (int mid) -> bool {
long long ans = 0;
int idx1 = index - (mid - 1); //mid减少到1的下标,左侧
if (idx1 < 0) {
//减少不到1
//只能减少到mid-index
int snode = mid - index;
int enode = mid;
int d = index + 1;
ans += ((long long)snode + enode) * d / 2;
} else {
//可以减少到1后保持1不变
int snode = 1;
int enode = mid;
int d = mid;
ans += ((long long)snode + enode) * d / 2;
ans += idx1;
}
int idx2 = index + (mid - 1); //mid减少到1的下标,右侧
if (idx2 >= n) {
//减少不到1
//只能减少到mid-(n-1-index)
int snode = mid - (n - 1 - index);
int enode = mid - 1;
int d = n-1 - (index + 1) + 1;
ans += ((long long)snode + enode) * d / 2;
} else {
//减少到1后保持不变
int snode = 1;
int enode = mid - 1;
int d = mid - 1;
ans += ((long long)snode + enode) * d / 2;
ans += n-idx2-1;
}
return ans <= maxSum;
};
int left = 1;
int right = maxSum;
int res = -1;
while (left <= right) {
int mid = ((long long)left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maxValue(self, n: int, index: int, maxSum: int) -> int:
def check(mid: int) -> bool:
nonlocal n, index, maxSum
ans = 0
idx1 = index - (mid - 1) #让mid减成1的下标,左侧
if idx1 < 0:
start = mid - index
end = mid
d = index + 1
ans += (start + end) * d // 2
else:
start = 1
end = mid
d = mid
ans += (start + end) * d // 2
ans += idx1
idx2 = index + (mid - 1) #让mid减成1的下标,右侧
if idx2 >= n:
start = mid - (n-1-index)
end = mid - 1
d = n-1 - (index + 1) + 1
ans += (start + end) * d // 2
else:
start = 1
end = mid - 1
d = mid - 1
ans += (start + end) * d // 2
ans += n - idx2 - 1
return ans <= maxSum
left = 1
right = maxSum
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目34:1642. 可以到达的最远建筑
解题思路:
(1)二分。
(2)在给定的bricks
和ladders
下,能够到达mid
。注意这个是如何判断的。
C++代码如下,
class Solution {
public:
int furthestBuilding(vector<int>& heights, int bricks, int ladders) {
function<bool(int)> check =[&] (int mid) -> bool {
//在bricks和ladders下,能够达到mid
vector<int> nums;
for (int i = 0; i < mid; ++i) {
int val = heights[i+1] - heights[i];
if (val > 0) {
nums.emplace_back(val);
}
}
sort(nums.begin(), nums.end());
int k1 = bricks, k2 = ladders;
for (auto x : nums) {
if (x <= k1) {
k1 -= x;
} else {
if (k2 > 0) {
k2 -= 1;
} else {
return false;
}
}
}
return true;
};
int n = heights.size();
int left = 0;
int right = n - 1;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int:
def check(mid: int) -> bool:
nonlocal heights, bricks, ladders
#在给定的bricks和ladders下,能够到达mid
nums = []
for i in range(mid):
val = heights[i+1] - heights[i]
if val > 0:
nums.append(val)
nums.sort()
k1, k2 = bricks, ladders
for x in nums:
if x <= k1:
k1 -= x
else:
#x > k1
if k2 > 0:
k2 -= 1
else:
return False
return True
n = len(heights)
left = 0
right = n-1
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目35:2861. 最大合金数
解题思路:二分,题目有些绕,理解题目。
C++代码如下,
class Solution {
public:
int maxNumberOfAlloys(int n, int k, int budget, vector<vector<int>>& composition, vector<int>& stock, vector<int>& cost) {
function<bool(int)> check =[&] (int mid) -> bool {
//使用stock和budge能制造出mid个合金
for (int i = 0; i < k; ++i) {
//使用第i台机器
long long ans = 0;
for (int j = 0; j < n; ++j) {
long long target = (long long)composition[i][j] * mid;
target -= stock[j];
if (target > 0) {
ans += target * cost[j];
}
}
if (ans <= budget) {
return true;
}
}
return false;
};
int left = 0;
int right = int(2e8+10);
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maxNumberOfAlloys(self, n: int, k: int, budget: int, composition: List[List[int]], stock: List[int], cost: List[int]) -> int:
def check(mid: int) -> bool:
#利用budget,stock能找出mid个合金
for i in range(k):
ans = 0
#使用第k台机器
nums = composition[i]
for j in range(len(nums)):
target = nums[j] * mid
target -= stock[j]
if target > 0:
ans += target * cost[j]
if ans <= budget:
return True
return False
left = 0
right = int(2e8 + 10)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
解题思路:二分。非最小值限制的数位DP。
C++代码如下,
class Solution {
public:
bool check(const long long& mid, const long long& k, const int& x) {
string s = bitset<64>(mid).to_string();
int n = s.size();
long long memo[68][68];
memset(memo, -1, sizeof memo);
function<long long(int,int,bool)> dfs =[&] (int i, int curr, bool is_limit) -> long long {
if (!is_limit && memo[i][curr] != -1) {
return memo[i][curr];
}
if (i == n) {
return curr;
}
long long res = 0;
int low = 0;
int up = is_limit ? s[i]-'0' : 1;
for (int d = low; d < up+1; ++d) {
if (d == 1 && (n-i) % x == 0) {
res += dfs(i+1, curr+1, is_limit && up == d);
} else {
res += dfs(i+1, curr, is_limit && up == d);
}
}
if (!is_limit) {
memo[i][curr] = res;
}
return res;
};
return dfs(0, 0, true) <= k;
}
long long findMaximumNumber(long long k, int x) {
long long left = 0;
long long right = (k+1)*(1<<x);
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid, k, x)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def findMaximumNumber(self, k: int, x: int) -> int:
def check(mid: int) -> bool:
#1~mid的总价值小于等于k
s = bin(mid)[2:]
n = len(s)
@cache
def dfs(i: int, curr: int, is_limit: bool) -> int:
if i == n:
return curr
res = 0
low = 0
up = int(s[i]) if is_limit else 1
for d in range(low,up+1):
if d == 1 and (n-i) % x == 0:
res += dfs(i+1, curr+1, is_limit and d == up)
else:
res += dfs(i+1, curr, is_limit and d == up)
return res
return dfs(0,0,True) <= k
left = 0
right = int((k+1)*2**x)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
解题思路:二分。假设让n
台电脑运行x
分钟,对于电量大于x
的电池,它只能使用x
分钟。因此,每个电池的使用时间为min(x, batteries[i])
。
C++代码如下,
class Solution {
public:
long long maxRunTime(int n, vector<int>& batteries) {
function<bool(long long)> check =[&] (long long mid) -> bool {
//在batteries下,这n台电脑可以运行mid分钟
long long ans = 0;
for (auto x : batteries) {
ans += min((long long)x, mid);
}
return mid * n <= ans;
};
long long left = 0;
long long right = 0;
for (auto x : batteries) {
right += x;
}
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maxRunTime(self, n: int, batteries: List[int]) -> int:
def check(mid: int) -> bool:
#能让n台电脑同时运行mid分钟
ans = 0
for x in batteries:
ans += min(mid, x)
return n * mid <= ans
left = 0
right = sum(batteries)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
题目38:2258. 逃离火灾
解题思路:先计算出火到达每个网格的最小时间f[i][j]
,然后二分判断,等待时间为startTime
是否能够到达安全屋。
C++代码如下,
class Solution {
public:
int maximumMinutes(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
vector<vector<int>> f(n, vector<int>(m, 1e9));
//先计算火到达每个网格的最小时间
queue<pair<int,int>> q;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (grid[i][j] == 1) {
q.push({i,j});
f[i][j] = 0;
}
}
}
while (!q.empty()) {
auto [x,y] = q.front();
q.pop();
for (int k = 0; k < 4; ++k) {
int nx = x + dirs[k][0];
int ny = y + dirs[k][1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (grid[nx][ny] == 2) continue;
if (f[nx][ny] != int(1e9)) continue;
f[nx][ny] = f[x][y] + 1;
q.push({nx,ny});
}
}
function<bool(int)> check =[&] (int startTime) -> bool {
vector<vector<int>> d(n, vector<int>(m, -1));
queue<pair<int,int>> q;
q.push({0,0});
d[0][0] = startTime;
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
for (int k = 0; k < 4; ++k) {
int nx = x + dirs[k][0];
int ny = y + dirs[k][1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (grid[nx][ny] == 2) continue;
if (d[nx][ny] != -1) continue; //已经访问过
//先判断是否走到终点
if (nx == n-1 && ny == m-1) {
if (d[x][y] + 1 <= f[nx][ny]) { //注意可以取等于号,“如果你到达安全屋后,火马上到了安全屋,这视为你能够安全到达安全屋”
return true;
}
}
if (d[x][y] + 1 < f[nx][ny]) {
d[nx][ny] = d[x][y] + 1;
q.push({nx,ny});
}
}
}
return false;
};
//二分答案
int left = 0;
int right = n * m;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
if (res == n * m) {
res = 1e9;
}
return res;
}
};
python3代码如下,
class Solution:
def maximumMinutes(self, grid: List[List[int]]) -> int:
n = len(grid)
m = len(grid[0])
dirs = [[-1,0],[1,0],[0,-1],[0,1]]
f = [[int(1e9)] * m for _ in range(n)] #每个网格火到达的最小时间
q = collections.deque()
for i in range(n):
for j in range(m):
if grid[i][j] == 1:
q.append([i,j])
f[i][j] = 0
while len(q) > 0:
x,y = q.popleft()
for k in range(4):
nx = x + dirs[k][0]
ny = y + dirs[k][1]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if grid[nx][ny] == 2:
continue
if f[nx][ny] != int(1e9): #访问过了
continue
f[nx][ny] = f[x][y] + 1
q.append([nx,ny])
#print(f)
def check(startTime: int) -> bool:
#print(f"check({startTime})")
#初始时间为startTime下,可以到达安全屋
d = [[-1] * m for _ in range(n)]
d[0][0] = startTime
q = collections.deque([[0,0]])
while len(q) > 0:
x, y = q.popleft()
for k in range(4):
nx = x + dirs[k][0]
ny = y + dirs[k][1]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if grid[nx][ny] == 2:
continue
if d[nx][ny] != -1: #访问过了
continue
if nx == n-1 and ny == m-1: #先判断是不是终点
if d[x][y] + 1 <= f[nx][ny]:
#print("return True!")
return True #能够抵达
if d[x][y] + 1 < f[nx][ny]:
d[nx][ny] = d[x][y] + 1
q.append([nx,ny])
#print("return False!")
return False
#二分答案
left = 0
right = n * m
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
#print(f"res = {res}.")
if res == n * m:
res = int(1e9)
return res
题目39:2071. 你可以安排的最多任务数目
解题思路:判断“mid个任务可以被完成”。把最简单的mid个任务分给最强的mid个工人,给恰能完成任务的人吃药。
C++代码如下,
class Solution {
public:
int maxTaskAssign(vector<int>& tasks, vector<int>& workers, int pills, int strength) {
sort(tasks.begin(), tasks.end());
sort(workers.begin(), workers.end());
function<bool(int)> check =[&] (int mid) -> bool {
//mid个任务可以被完成
int n = tasks.size();
int m = workers.size();
vector<int> new_tasks(tasks.begin(), tasks.begin()+mid);
int offset = m - mid;
vector<int> new_workers(workers.begin()+offset, workers.end());
int cnt = pills;
for (int i = mid-1; i >= 0; --i) {
int task = new_tasks[i];
if (new_workers.back() >= task) {
new_workers.pop_back();
} else {
if (cnt > 0) {
auto iter = lower_bound(new_workers.begin(), new_workers.end(), task-strength);
if (iter == new_workers.end()) {
return false;
} else {
new_workers.erase(iter);
cnt -= 1;
}
} else {
return false;
}
}
}
return true;
};
int left = 0;
int right = min(tasks.size(), workers.size());
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maxTaskAssign(self, tasks: List[int], workers: List[int], pills: int, strength: int) -> int:
tasks.sort()
workers.sort()
def check(mid: int) -> bool:
#print(f"check({mid})")
#有mid个任务可以被完成
new_tasks = tasks[0:mid] #最简单的mid个任务
new_workers = workers[-mid:] #最强的mid个工人
cnt = pills #可以使用增强药的数目
for i in range(mid-1,-1,-1):
task = new_tasks[i]
if new_workers[-1] >= task: #最强的工人能完成
del new_workers[-1]
else:
if cnt > 0: #有增强药
idx = bisect.bisect_left(new_workers, task-strength)
if idx == len(new_workers):
#print("return false")
return False
else:
del new_workers[idx]
cnt -= 1
else:
#print("return false")
return False
#print("return true")
return True
left = 0
right = min(len(tasks),len(workers))
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
return res
2.4 二分间接值
题目40:3143. 正方形中的最多点数
解题思路:先二分出正方形边长的一半,然后在计算正方形内部的点数。
C++代码如下,
class Solution {
public:
int maxPointsInsideSquare(vector<vector<int>>& points, string s) {
int n = points.size();
function<bool(int)> check =[&] (int mid) -> bool {
//正方形边长为2*mid,此正方形内部没有重复类型的点
set<char> types;
for (int i = 0; i < n; ++i) {
int x = points[i][0], y = points[i][1];
char t = s[i];
int maxval = max(abs(x), abs(y));
if (maxval <= mid) {
if (types.find(t) == types.end()) {
types.insert(t);
} else {
return false;
}
}
}
return true;
};
int left = 0;
int right = int(1e9+10);
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
//正方向边长为2*res,此正方形内部有多少个点
int cnt = 0;
for (auto point : points) {
int x = point[0];
int y = point[1];
int maxval = max(abs(x), abs(y));
if (maxval <= res) {
cnt += 1;
}
}
return cnt;
}
};
python3代码如下,
class Solution:
def maxPointsInsideSquare(self, points: List[List[int]], s: str) -> int:
def check(mid: int) -> bool:
#边长为mid*2的正方形内,是否有重复类型的点
types = set()
n = len(points)
for i in range(n):
x,y = points[i]
t = s[i]
maxval = max(abs(x),abs(y))
if maxval <= mid:
if t in types:
return False
else:
types.add(t)
return True
left = 0
right = int(1e9+10)
res = -1 #正方形边长的一半
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
left = mid + 1
else:
right = mid - 1
#边长为res*2下,有多少个点
cnt = 0
for x,y in points:
maxval = max(abs(x),abs(y))
if maxval <= res:
cnt += 1
return cnt
题目41:1648. 销售价值减少的颜色球
解题思路:贪心。从大到小排序,每个元素减少至下一个元素,有多个这样的过程。可以往inventory
中添加元素0
,充当哨兵,表示所有球都卖出的情况。
C++代码如下,
class Solution {
public:
int maxProfit(vector<int>& inventory, int orders) {
const int mod = 1e9 + 7;
long long res = 0;
inventory.emplace_back(0);
sort(inventory.begin(), inventory.end(), greater<int>());
int n = inventory.size();
for (auto x : inventory) {
cout << x << " ";
}
cout << endl;
for (int i = 0; i < n-1; ++i) {
//商品价格从inventory[i]减少至inventory[i+1]+1,有i+1个这样的商品
long long m = 1ll * (inventory[i] - inventory[i+1]) % mod * (i + 1); //带来的总数量
if (m <= orders) {
long long cost = 1ll * (inventory[i] + inventory[i+1]+1) * (inventory[i] - inventory[i+1]) / 2;
cost %= mod;
cost = cost * (i+1) % mod;
res = (res + cost) % mod;
orders -= m;
} else {
int m1 = orders / (i+1);
//商品价格从inventory[i]减少至inventory[i]-m1+1,有i+1个这样的商品
long long cost1 = 1ll * (inventory[i] + inventory[i]-m1+1) * m1 / 2;
cost1 %= mod;
cost1 = cost1 * (i+1) % mod;
res = (res + cost1) % mod;
int m2 = orders % (i+1);
//商品价格为inventory[i]-m1恒定不变,有m2个这样的商品
long long cost2 = 1ll * (inventory[i]-m1) * m2 % mod;
res = (res + cost2) % mod;
break;
}
}
return res;
}
};
python3代码如下,
class Solution:
def maxProfit(self, inventory: List[int], orders: int) -> int:
inventory.append(0) #添加一个哨兵,表示卖完所有球后
inventory.sort(reverse=True)
n = len(inventory)
res = 0
for i in range(n-1):
m = (inventory[i] - inventory[i+1]) * (i + 1) #假定商品价格可以压到inventory[i+1]+1,可以卖出的商品总数量
if m <= orders:
#卖出商品的总价值
cost = (inventory[i] + inventory[i+1] + 1) * (inventory[i] - inventory[i+1]) // 2 * (i + 1)
res += cost
orders -= m
else:
m1 = orders // (i+1) #商品价格可以压到多少
#商品价格可以压到inventory[i]-m1+1
cost1 = (inventory[i] + inventory[i]-m1+1) * m1 // 2 * (i + 1)
m2 = orders % (i+1) #商品价格可以压到inventory[i]-m1
cost2 = (inventory[i]-m1) * m2
res += cost1 + cost2
break
res %= int(1e9+7)
return res
2.5 最小化最大值
题目42:410. 分割数组的最大值
解题思路:二分。可以将数组nums
分隔成k
组,且每组的和都小于等于mid
。
C++代码如下,
class Solution {
public:
int splitArray(vector<int>& nums, int k) {
function<bool(int)> check =[&] (int mid) -> bool {
//可以将nums分隔成k个子数组,且每个子数组的和都小于等于mid
int cnt = 1;
int tot = 0;
for (auto num : nums) {
tot += num;
if (tot > mid) {
cnt += 1;
tot = num;
if (cnt > k) {
return false;
}
} else {
//pass
}
}
return true;
};
int left = *std::ranges::max_element(nums);
int right = accumulate(nums.begin(), nums.end(), 0);
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def splitArray(self, nums: List[int], k: int) -> int:
def check(mid: int) -> bool:
#可以将数组分隔成k组,且每组的和都小于等于mid
cnt = 1 #注意cnt是从1开始的
curr = 0
for num in nums:
curr += num
if curr > mid:
cnt += 1
curr = num
if cnt > k:
return False
else:
pass
return True
left = max(nums)
right = sum(nums)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目43:2064. 分配给商店的最多商品的最小值
解题思路:二分。将所有商品分配给零售店,每个零售店获得的商品的数目都小于等于mid
。
C++代码如下,
class Solution {
public:
int minimizedMaximum(int n, vector<int>& quantities) {
function<bool(long long)> check =[&] (long long mid) -> bool {
//将商品分给所有商店,商店获得的商品数量的最大值<=mid
long long cnt = 0;
for (auto x : quantities) {
cnt += 1ll * (x+mid-1) / mid;
}
return cnt <= n;
};
long long right = 0;
for (auto x : quantities) right += x;
long long left = (right+n-1)/n;
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimizedMaximum(self, n: int, quantities: List[int]) -> int:
def check(mid: int) -> bool:
#将所有商品分配给零售店,每个零售店获得的商品的数目都小于等于mid
cnt = 0
for x in quantities:
cnt += (x + mid - 1) // mid
return cnt <= n
left = (sum(quantities) + (n-1)) // n
right = sum(quantities)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid + 1
return res
题目44:1760. 袋子里最少数目的球
解题思路:二分。
C++代码如下,
class Solution {
public:
int minimumSize(vector<int>& nums, int maxOperations) {
int n = nums.size() + maxOperations;
function<bool(long long)> check =[&] (long long mid) -> bool {
//将nums分配给n个袋子,每个袋子中球的数量都<=mid
long long cnt = 0;
for (auto x : nums) {
cnt += 1ll * (x+mid-1) / mid;
}
return cnt <= n;
};
long long left = 0;
for (auto x : nums) left += x;
left = (left+n-1) / n;
long long right = ranges::max(nums);
long long res = -1;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid-1;
} else {
left = mid+1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimumSize(self, nums: List[int], maxOperations: int) -> int:
def check(mid: int) -> bool:
#将nums分配给n个袋子,每个袋子中球的数量<=mid
cnt = 0
for x in nums:
cnt += (x+mid-1) // mid
return cnt <= n
n = len(nums) + maxOperations
left = (sum(nums)+n-1) // n
right = max(nums)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid+1
return res
题目45:1631. 最小体力消耗路径
解题思路:二分。
C++代码如下,
typedef pair<int,int> PII;
class Solution {
public:
int minimumEffortPath(vector<vector<int>>& heights) {
int n = heights.size();
int m = heights[0].size();
int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
function<bool(int)> check =[&] (int mid) -> bool {
//mid体力下,可以走到终点
vector<vector<int>> st(n, vector<int>(m, false));
queue<PII> q;
q.push(make_pair(0,0));
st[0][0] = true;
while (!q.empty()) {
pair<int,int> t = q.front();
q.pop();
int x = t.first, y = t.second;
if (x == n-1 && y == m-1) return true; //走到了终点
for (int k = 0; k < 4; ++k) {
int nx = x + dirs[k][0];
int ny = y + dirs[k][1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (abs(heights[x][y]-heights[nx][ny]) > mid) continue;
if (st[nx][ny]) continue; //走过了
q.push(make_pair(nx,ny));
st[nx][ny] = true;
}
}
return false;
};
int left = 0;
int right = 1e6;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid-1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimumEffortPath(self, heights: List[List[int]]) -> int:
dirs = [[-1,0],[1,0],[0,-1],[0,1]]
def check(mid: int) -> bool:
#体力为mid,能走到右下角
n = len(heights)
m = len(heights[0])
st = [[False]*m for _ in range(n)]
q = collections.deque()
q.append([0,0])
st[0][0] = True
while len(q) > 0:
x,y = q.popleft()
if x == n-1 and y == m-1: #走到了终点
return True
for k in range(4):
nx = x + dirs[k][0]
ny = y + dirs[k][1]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if abs(heights[x][y]-heights[nx][ny]) > mid:
continue
if st[nx][ny]: #走过了
continue
q.append([nx,ny])
st[nx][ny] = True
return False
left = 0
right = int(1e6)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid + 1
return res
题目46:2439. 最小化数组中的最大值
解题思路:二分。
C++代码如下,
class Solution {
public:
int minimizeArrayValue(vector<int>& nums) {
function<bool(int)> check =[&] (int mid) -> bool {
//nums中每个数都<=mid
int n = nums.size();
long long overpass = 0;
for (int i = n-1; i >= 0; --i) {
if (nums[i] > mid) {
overpass += nums[i] - mid;
} else if (nums[i] == mid) {
//pass;
} else {
int x = mid - nums[i];
overpass -= x;
overpass = max(overpass, 0ll);
}
}
return overpass == 0;
};
int left = 0;
int right = ranges::max(nums);
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid-1;
} else {
left = mid+1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimizeArrayValue(self, nums: List[int]) -> int:
def check(mid: int) -> int:
#nums中的每个元素都<=mid
n = len(nums)
overpass = 0
for i in range(n-1,-1,-1):
if nums[i] > mid:
overpass += nums[i]-mid
elif nums[i] == mid:
pass
else:
x = mid - nums[i]
if overpass >= x:
overpass -= x
else:
overpass = 0
return overpass == 0
left = 0
right = max(nums)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid+1
return res
题目47:2560. 打家劫舍 IV
解题思路:二分。从数组中选出<=mid
的所有的数,注意不能选择相邻的,总数目>=k
。
C++代码如下,
class Solution {
public:
int minCapability(vector<int>& nums, int k) {
function<bool(int)> check =[&] (int mid) -> bool {
//从nums中选择<=mid的数,非相邻,最终个数>=k
int cnt = 0;
int n = nums.size();
int i = 0;
while (i < n) {
if (nums[i] <= mid) {
cnt += 1;
i += 2;
} else {
i += 1;
}
}
return cnt >= k;
};
int left = ranges::min(nums);
int right = ranges::max(nums);
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid-1;
} else {
left = mid+1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minCapability(self, nums: List[int], k: int) -> int:
def check(mid: int) -> bool:
#从nums中选择出k个不相邻的数,存在一种选法,它们的值都<=mid
cnt = 0
i = 0
while i < len(nums):
if nums[i] <= mid:
cnt += 1
i += 2
else:
i += 1
return cnt >= k
left = min(nums)
right = max(nums)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid+1
return res
题目48:778. 水位上升的泳池中游泳
解题思路:二分。
C++代码如下,
class Solution {
public:
int swimInWater(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
int dirs[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
function<bool(int)> check =[&] (int mid) -> bool {
//最大值为mid,能否走到终点
if (grid[0][0] > mid) return false; //特判
vector<vector<bool>> st(n, vector<bool>(m, false));
queue<pair<int,int>> q;
q.push(make_pair(0,0));
st[0][0] = true;
while (!q.empty()) {
auto t = q.front();
int x = t.first, y = t.second;
q.pop();
if (x == n-1 && y == m-1) return true; //能走到终点
for (int k = 0; k < 4; ++k) {
int nx = x + dirs[k][0];
int ny = y + dirs[k][1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (grid[nx][ny] > mid) continue;
if (st[nx][ny]) continue;
q.push(make_pair(nx,ny));
st[nx][ny] = true;
}
}
return false;
};
int left = 0;
int right = 2500;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid-1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def swimInWater(self, grid: List[List[int]]) -> int:
n = len(grid)
m = len(grid[0])
dirs = [[-1,0],[1,0],[0,-1],[0,1]]
def check(mid: int) -> bool:
#体力为mid,可以到达终点
if grid[0][0] > mid: #特判
return False
st = [[False]*m for _ in range(n)]
q = collections.deque()
q.append([0,0])
st[0][0] = True
while len(q) > 0:
x,y = q.popleft()
if x == n-1 and y == m-1:
return True #能走到终点
for k in range(4):
nx = x + dirs[k][0]
ny = y + dirs[k][1]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if grid[nx][ny] > mid:
continue
if st[nx][ny]: #访问过了,跳过
continue
q.append([nx,ny])
st[nx][ny] = True
return False
left = 0
right = 2500
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid - 1
else:
left = mid + 1
return res
题目49:2616. 最小化数对的最大差值
解题思路:二分。
C++代码如下,
class Solution {
public:
int minimizeMax(vector<int>& nums, int p) {
int n = nums.size();
ranges::sort(nums);
function<bool(int)> check =[&] (int mid) -> bool {
//差值<=mid,有cnt个,且cnt>=p
int cnt = 0;
int i = 0;
while (i < n-1) {
int x = nums[i+1]-nums[i];
if (x <= mid) {
cnt += 1;
i += 2;
} else {
i += 1;
}
}
return cnt >= p;
};
int left = 0;
int right = 1e9;
int res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (check(mid)) {
res = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return res;
}
};
python3代码如下,
class Solution:
def minimizeMax(self, nums: List[int], p: int) -> int:
n = len(nums)
nums.sort()
def check(mid: int) -> bool:
#差值小于等于mid的数对,能找到cnt对,且cnt >= p
cnt = 0
i = 0
while i < n-1:
x = nums[i+1]-nums[i]
if x <= mid:
cnt += 1
i += 2
else:
i += 1
return cnt >= p
left = 0
right = int(1e9)
res = -1
while left <= right:
mid = (left + right) // 2
if check(mid):
res = mid
right = mid-1
else:
left = mid+1
return res