算法步骤
- 估计答案可能得范围是什么
- 分析问题的答案和给定条件之间的单调性
- 建立 check 函数,判断在答案固定的情况下条件是否达标
- 在可行的范围内不断二分找到最适合的答案
最大最小
https://leetcode.cn/problems/split-array-largest-sum/description/
class Solution {
public:
int splitArray(vector<int>& nums, int k) {
long sum = 0;
for (int x : nums)
sum += x;
long ans = 0;
long l = *max_element(nums.begin(), nums.end());
long r = sum;
while (l <= r) {
long m = (l + r) / 2;
long need = check(nums, m);
if (need <= k)
{
// 为什么小于等于的都要记录?
/*
7 5
2 3 1 1 1 1 1
这种情况最后可能check回来得到的答案是4,但是可以拆成五个又不影
响最大值,也就是如果最后的这个 ans 由 check 函数求出来不是 k,
那么一定可以拆成 k 个且不影响最大值,拆成 k 个是必然的,因为 k
始终小于等于 n ,如果影响了最大值,那么说明最大值还可以更小,这与
ans 是最终答案矛盾。
*/
ans = m;
r = m - 1;
} else
l = m + 1;
}
return int(ans);
}
int check(const vector<int>& nums, int limit) {
int need = 1;
int sum = 0;
for (int x : nums) {
if (sum + x > limit)
need++, sum = x;
else
sum += x;
}
return need;
}
};
找出第k小数对的距离
考虑二分,check函数采用双指针的方式来找。
class Solution {
public:
int smallestDistancePair(vector<int>& nums, int k) {
sort(nums.begin(),nums.end()); // 升序排列
int n=nums.size();
int l=0,r=nums[n-1]-nums[0]; // 确定边界
int ans=0;
while(l<=r) // 二分找第 k 小的数
{
int mid=(l+r)>>1;
int cnt=check(nums,mid);
if(cnt>=k) // 如果当前数对的个数大于 k 往左边找
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
return ans;
}
int check(const vector<int>nums,int m)
{
int ans=0;
int r=0;
for(int l=0;l<nums.size();l++)
{
while(r+1<nums.size()&&nums[r+1]-nums[l]<=m)
r++;
ans+=r-l;
}
return ans;
}
};
class Solution {
public:
long long maxRunTime(int n, vector<int>& batteries) {
long long l=0,r=0;
for(int x:batteries) // 确定右边界,即所有电池的总电量,由于电脑数很可能比电池的个数要少,因此用所有电池的总电量来作为右边界
r+=x;
long long ans;
while(l<=r)
{
long long mid=l+(r-l)/2;
if(check(batteries,mid,n))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
bool check(vector<int>&batteries,long long limit,int num)
{
long long sum=0;
for(int x:batteries)
{
if(x>=limit) num--; // 对于电量大于 limit 的电池,多出来的电量没法用给其他的电脑。
else sum+=x;
if(sum>=(long long)limit*num) return true;
}
return false;
}
};