二分中位数
一道比较有意思的二分题
题面
给定
你需要最大化你选择的卡片的中位数,并输出这个最大值。这里的中位数是
思路
不是那么显然的二分。考虑一个数
最后看如果
假设我们用
然后我们又不难注意到,随着
因为两者都单调不降且我们要找的是最大中位数,所以我们只需要找到从右到左第一个
然后就二分做了。
展示两种种二分思路。
最后一个
int n,a[200005],b[200005],f[2][2]; bool check(int x){ f[0][0]=0,f[1][0]=0; int o=1; for(int i=1;i<=n;i++,o^=1){ f[0][o]=f[1][o^1]; f[1][o]=max(f[0][o^1],f[1][o^1])+(a[i]>x?1:-1); } return max(f[0][o^1],f[1][o^1])<=0; } int main(){ n=read(); for(int i=1;i<=n;i++) b[i]=a[i]=read(); sort(b+1,b+1+n); int l=1,r=n; while(l<=r){ int mid=(l+r)>>1; if(check(b[mid]))r=mid-1; else l=mid+1; } //此时 r 指向的是最后一个 <0 的,那么 l 指向的就是第一个 >= 0的 cout<<b[l]; }
或者保险一点直接找答案的:
//相同代码略 int main(){ //... while(l<r){ int mid=(l+r)>>1; if(check(b[mid]))r=mid;//让 r 指向最后一个满足条件的 else l=mid+1; } cout<<b[r]; }
其实可以不用离散的,因为从右到左第一个可以的一定在原数组里嘛(假设不在,那么说明有一个更大的在数组里面的和这个值等效,那我肯定选更大的在数组里的对吧)。
int n,a[200005],f[2][2]; bool check(int x){ f[0][0]=0,f[1][0]=0; int o=1; for(int i=1;i<=n;i++,o^=1){ f[0][o]=f[1][o^1]; f[1][o]=max(f[0][o^1],f[1][o^1])+(a[i]>x?1:-1); } return max(f[0][o^1],f[1][o^1])<=0; } int main(){ n=read(); for(int i=1;i<=n;i++) a[i]=read(); int l=0,r=1e9+3; while(l<=r){ int mid=(l+r)>>1; if(check(mid))r=mid-1; else l=mid+1; } cout<<l; }
后记
这道题困扰了我挺久的,一开始听说可以用奇怪的二分写然后就在信息课上随便打了一个没有离散,乱写版二分,小样例过了直接交然后发现过了。非常疑惑,不理解这凑巧的二分为什么能过,然后自己想了挺久的,所以这里就算是记录一下证明过程吧(又浪费了下午为数不多的三节竞赛课中的两节,受不了)。
end:2023.10.6 16:10
本文作者:NBestの思潮
本文链接:https://www.cnblogs.com/NBest/p/17744674.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步