闲着没事干想写一篇关于简单二分的解析
题意:
思路:
求最值一般有三种思路:
贪心
二分
Dp
这道题用二分答案
我们考虑把最小代价二分出来
能使用二分的前提是单调性
在这道题中,如果代价足够大(先假设代价是无穷大),那么A数组变成B数组就没有任何限制,一定可以变成B数组
然后,考虑把代价减小一点点(但是仍然是很大),A数组还是很有可能变成B数组,因为此时限制还是很小,能使用的代价还是很多
继续减小这个代价,极端地考虑,减到0,此时A数组一定不能变成B数组,因为此时能供你使用的代价为0,A数组不能作任何改变
那么,这个代价从无穷大减小到无穷小的过程中,一定存在某一点,使得它刚好从“能变成B数组”变成“不能变成B数组”
我们要求的就是这个“临界点”,这个“临界点”就是最小代价
与其说是最小代价,其实是指:求最大代价的最小值
像这种求最大的最小值,或求最小的最大值,一般都是用二分答案做
这个0表示不能把A数组变成B数组
1表示可以
X就是我们要求的临界点
如果二分出来的是0,那么l=mid+1;
如果二分出来的是1,那么r=mid-1;
那么我们二分出来一个值后,如何判断它是1还是0?
即
假设我们二分出代价 x ,我们去判断这个 x 够不够将A数组变成B数组
在这个判断过程中,一般要用到贪心的思想
要让A数组更容易变成B数组,A数组一定是越小越好
因此我们先将a[1]-=x,第一个数变小之后,因为是严格递增的,后面的数也可以相应地变小
然后我们去考虑后面的数
如果a[2]比a[1]小且a[2]<a[1]-x,即a[2]+x<a[1](x为二分出来的代价),那么a[2]就不能变成比a[1]大的数,直接return false即可,
如果a[2]比a[1]小且a[2]>=a[1]-x,即a[2]+x>=a[1](x为二分出来的代价),那么a[2]只要加上x就能变成a[1],因此将a[2]变成a[1]+1就好了,(因为要让A数组尽可能小而又要满足严格递增,贪心的思想)
如果a[2]比a[1]大且a[2]>a[1]+x,即a[2]-x>a[1](x为二分出来的代价),那么要让A数组尽可能小,就让a[2]-=x;
如果a[2]比a[1]大且a[2]<a[1]+x,即a[2]-x<a[1](x为二分出来的代价),那么要让A数组尽可能小,就让a[2]变成a[1]+1即可
后面的a[i]都可以从a[i-1]这样递推而来
#include <bits/stdc++.h>
using namespace std;
const int mxn=1e5+10;
int n,ans,T=0;
int a[mxn],b[mxn];
bool check(int x){//判断二分出来的值是0还是1
memcpy(b,a,sizeof(b));//为了不破坏a数组,设一个新的b数组,从a数组那边复制过来
b[1]-=x;
for(int i=2;i<=n;i++){
if(b[i]<=b[i-1]){
if(b[i]+x>b[i-1]) b[i]=b[i-1]+1;
else return false;
}else if(b[i]-x>b[i-1]) b[i]-=x;
else b[i]=b[i-1]+1;
}
return true;
}
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int l=0,r=1e9;
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
ans=mid;
r=mid-1;
}else l=mid+1;
}
printf("Case #%d:\n%d\n",++T,ans);
}
int main(){
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}