每日一题:二分答案

 二分答案 - 题目 - 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
#include
#include
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(防止死循环)

你可能感兴趣的:(每日一题,算法,二分答案,c++)