二分答案 - 题目 - Daimayuan Online Judge
首先读入 n 和 k,然后读入序列 a。
接下来使用 l 和 r 表示最小值的猜测区间。由于题目中规定了最小值和元素范围,因此我们可以将上界设置为 1e18,下界设置为 1
二分查找的过程中,我们每次猜测一个中间值 mid,并判断是否满足条件(即 check 函数)。如果满足条件,说明当前的猜测值可能是答案,我们将猜测区间的左端点移动到 mid 上;否则,说明当前的猜测值不可能是答案,我们将猜测区间的右端点移动到 mid - 1 上
check 函数表示,对于当前猜测的最小值 x,统计一下将原序列中所有元素都变为 x 的最少操作次数 cnt,看看是否小于等于 k。具体地,如果 a[i] < x,那么我们需要对 a[i] 进行(x - a[i]) 次操作,否则不需要任何操作。
最终,l 就是答案
注意:区间上限r可以用1e13+1e8+10表示,也不用太大到1e18
判断cnt与k的大小关系,每次都要判断一次,否则如果r=1e18,取的中间值mid过大,数组a每个数和mid差的太大,全部的差加起来之后超过了long long范围,变成了负数,这样最后再判断的话,负数cnt肯定小于k,这样就出错了
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int N = 100010;
int a[N];
int n, k;
bool check(int x) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (a[i] < x) {
cnt += x - a[i];
if (cnt > k) return false;
}
}
return true;
}
signed main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
int l = 1, r = 1e18;
while (l < r) {
int mid = (l + r+1) / 2;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l << endl;
}
if(check(mid))
l=mid or r=mid
最后统一写return l(事实上,r也行,因为最后l和r相等)
书写步骤:
先写上mid=l+r>>1;
然后写if(if里面都写答案所在的那个区间),根据if写l=mid还是r=mid
最后如果是l=mid,那么最前面变成mid=l+r+1>>1(防止死循环),如果是r=mid,那么前面仍然是l+r>>1(防止死循环)