灵神力扣题单之二分算法(上)

57 阅读45分钟

1 介绍

本博客用来记录灵神力扣题单之二分算法

2 训练

2.1 二分查找

题目134. 在排序数组中查找元素的第一个和最后一个位置

解题思路:二分查找。

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] 

题目235. 搜索插入位置

解题思路:二分查找。

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)

题目3704. 二分查找

解题思路:二分算法。

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 

题目4744. 寻找比目标字母大的最小字母

解题思路:二分算法。

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] 

题目52529. 正整数和负整数的最大计数

解题思路:二分算法。

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)

题目61385. 两个数组间的距离值

解题思路:二分算法。

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 

题目72300. 咒语和药水的成功对数

解题思路:手写二分算法。

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 

题目82389. 和有限的最长子序列

解题思路:二分算法。

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 

题目91170. 比较字符串最小字母出现频次

解题思路:模拟+二分。

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 

题目102080. 区间内查询数字的频率

解题思路:模拟+二分。对于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)

题目112563. 统计公平数对的数目

解题思路:二分+双指针。或者仅二分。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 

题目122856. 删除数对后的最小数组长度

解题思路:哈希表。

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 

题目13981. 基于时间的键值存储

解题思路:二分。

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)

题目141146. 快照数组

解题思路:存储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)

题目151818. 绝对差值和

解题思路:二分。注意最后再取模。

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)

题目16911. 在线选举

解题思路: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)

题目17LCP 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 二分答案:求最小

题目181283. 使结果不超过阈值的最小除数

解题思路:自己实现二分。

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 

题目192187. 完成旅途的最少时间

解题思路:二分。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 

题目201870. 准时到达的列车最小时速

解题思路:二分。注意读题,生成的测试用例保证答案不超过 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 

题目211011. 在 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 

题目22875. 爱吃香蕉的珂珂

解题思路:二分。

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 

题目23475. 供暖器

解题思路:二分。

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 

题目242594. 修车的最少时间

解题思路:二分。

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 

题目251482. 制作 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 

题目263048. 标记所有下标的最早秒数 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 

题目273049. 标记所有下标的最早秒数 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 二分答案:求最大

题目28275. 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 

题目292226. 每个小孩最多能分到多少糖果

解题思路:二分。

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 

题目302982. 找出出现至少三次的最长特殊子字符串 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

题目312576. 求出最多标记下标

解题思路:二分。

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

题目321898. 可移除字符的最大数目

解题思路:二分。

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 
        

题目331802. 有界数组中指定下标处的最大值

解题思路:

(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 

题目341642. 可以到达的最远建筑

解题思路:

(1)二分。

(2)在给定的bricksladders下,能够到达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 

题目352861. 最大合金数

解题思路:二分,题目有些绕,理解题目。

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 

题目363007. 价值和小于等于 K 的最大数字

解题思路:二分。非最小值限制的数位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 

题目372141. 同时运行 N 台电脑的最长时间

解题思路:二分。假设让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 

题目382258. 逃离火灾

解题思路:先计算出火到达每个网格的最小时间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 

题目392071. 你可以安排的最多任务数目

解题思路:判断“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 二分间接值

题目403143. 正方形中的最多点数

解题思路:先二分出正方形边长的一半,然后在计算正方形内部的点数。

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 

题目411648. 销售价值减少的颜色球

解题思路:贪心。从大到小排序,每个元素减少至下一个元素,有多个这样的过程。可以往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 最小化最大值

题目42410. 分割数组的最大值

解题思路:二分。可以将数组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 

题目432064. 分配给商店的最多商品的最小值

解题思路:二分。将所有商品分配给零售店,每个零售店获得的商品的数目都小于等于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 

题目441760. 袋子里最少数目的球

解题思路:二分。

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 

题目451631. 最小体力消耗路径

解题思路:二分。

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 

题目462439. 最小化数组中的最大值

解题思路:二分。

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 

题目472560. 打家劫舍 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 

题目48778. 水位上升的泳池中游泳

解题思路:二分。

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 

题目492616. 最小化数对的最大差值

解题思路:二分。

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 

3 参考

灵神力扣题单汇总