【二分答案】
【二分答案】
二分+贪心 重点在贪心(check函数)
思路
只要满足单调性就都可以二分答案!!!
单调性:假设x为答案 x左边一个性质 x右边一个性质
可优化O(n) -> O(logn)
【题目整理】
跳石头
https://ac.nowcoder.com/acm/contest/22353/C
重点 设计check函数
代码
#include<bits/stdc++.h>
using namespace std;
const int N=50010;
long long l,d[N],jian[N];
int m,n;
//如何检查:为达成x必须要移走的石头(模拟!!!)
bool check(long long x){
int tot=0;//当前需要挪走的石头数量
int i=0;//下一个要跳到的石头
int now=0;//现在在的石头
while(i<=n){//跳到终点才算结束
i++;
if((d[i]-d[now])<x){//要把这块石头挪走
tot++;
}
else{//跳过去
now=i;
}
}
//判断该x下需要挪走的石头数是否大于原先计划
if(tot>m) return false;
else return true;
}
int main(){
cin>>l>>n>>m;
for(int i=1;i<=n;i++){//从1开始读:有第0块石头和第n+1块石头的存在
cin>>d[i];
}
d[n+1]=l;//最后一个石头要存!
//二分查找最短跳跃距离 找最大值(向右查找)
long long ll=1,rr=l;
while(ll<rr){
long long mid=(ll+rr+1)/2;
if(check(mid)) ll=mid;
else rr=mid-1;
}
cout<<ll;
return 0;
}
Greedy Gift Takers
https://ac.nowcoder.com/acm/contest/22353/D
重点
看着和二分没关系的题->答案可以一个个找->二分降复杂度
->验证找到的答案的准确性->设计check函数
//拿不到礼物的一定是后面的
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n;
int c[N],b[N];
int ans=0;
bool check(int x){
int tail=n-x;//如果前x头牛能够拿到礼物:拿完后一定排在后面->记录插到队尾往前数的第几个
for(int i=1;i<x;i++) b[i]=c[i];//前i-1(第i头正好拿到)头牛的信息->排序找
sort(b+1,b+x);
for(int i=1;i<x;i++,tail++){//注意插几个要同步移动->前面的点移到后面 界限更多
if(b[i]>tail) return false;//超过界限->牛需要插到更前面的位置->牛无法插到合适的位置
}
return true;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
}
int l=1,r=n;
//找收得到礼物的最大值
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
ans=n-l;
printf("%d",ans);
return 0;
}
整点巧克力
https://ac.nowcoder.com/acm/contest/22353/1003
重点
在找到答案之后还需要check一遍找正确的吃巧克力序列
//跳石子思想
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010;
int n,d;
ll a[N],l=0,r;
int s[N];//每次跑都更新一遍
bool check(ll x){//在保持比x大的状态下能坚持多少天
ll sum=0;
int cnt=0;
for(int i=1;i<=d;i++){//枚举天数
while(sum<x){
sum+=a[++cnt];
if(cnt>n) return false;//巧克力数不够
s[cnt]=i;
}
sum>>=1;
}
for(int i=cnt+1;i<=n;i++) s[i]=d;
return true;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>d;
for(int i=1;i<=n;i++){
cin>>a[i];
r+=a[i];
}
//二分答案
while(l<r){
ll mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<'\n';
check(l);//最后还需要调用一遍答案->来更新s找到第几天吃的
for(int i=1;i<=n;i++) cout<<s[i]<<'\n';
return 0;
}
智乃的小球
https://ac.nowcoder.com/acm/contest/95335/E
思路
小球相碰直接当成穿过
小球相遇所需时间就是距离/2
->找第k小的距离->距离满足单调性
->二分距离 验证该距离下k与所求k的关系
代码
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
typedef long double ld;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const ll INF=1000000002;
int n;
ll k;
vector<ll> u,v;
/*
【思路】
小球相撞->直接当成穿过去就行
有单调性:距离增加的趋势->二分
->check函数写在该距离下能碰撞几次:<k 答案给小了 >k 答案给大了
*/
ll check(ll t){
ll res=0;
int p1=0,p2=0;//找右边第一个和距离内最后一个
for(auto &i:u){
//v下标从0开始(x
while(p1<v.size() && v[p1]<i) p1++;
while(p2<v.size() && v[p2]<=i+t) p2++;
res+=p2-p1;
}
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
ll p,q;
cin>>p>>q;
if(q==1) u.push_back(p);
else v.push_back(p);
}
sort(u.begin(),u.end());
sort(v.begin(),v.end());
ll lu=u.size(),lr=v.size();
//二分板子没问题
ll l=0,r=1e9+2;
while(l<r){
ll mid=(l+r)/2;
if(check(mid)>=k) r=mid;
else l=mid+1;
}
ld ans=(ld)r/2;
if(r==INF) cout<<"No"<<endl;//答案趋于无限大:找不到数->无解(不用想无解结论(x
else{
cout<<"Yes"<<endl;
cout<<fixed<<setprecision(6)<<ans<<endl;
}
return 0;
}
中位数
https://qoj.ac/contest/2182/problem/12367
题目大意
每次可以选3个相邻的数合并成他们的中位数
问最后留下来的那个数
思路:【转化问题】
(1)最后结果要最大->二分答案
(2)中位数一般处理方法 -> >=mid就设成1 <mid就设成0
->check贪心思路:去掉尽可能多的0 让1的数量>0的数量->合并完才有可能是1
->为了保证[1的数量>0的数量]:两种合并方式出0->压缩0的个数
要么三个0 要么2个0 1个1
① 000->0
② 010->0 这种是没办法必须合并
(注意不会有100这种情况需要合并的
要么是0100 有010已经合并过了
要么是11000 等3个0一起合并
(尽量不要001就合并 如果有0011这样就不需要消耗1的个数
代码
int n;
vector<int> a;
//000
void upgrade1(vector<int> &res){
if(res.size()>=3){
int l=res.size();
int cnt1=res[l-1],cnt2=res[l-2],cnt3=res[l-3];
if(cnt1==0 && cnt2==0 && cnt3==0){
res.pop_back();
res.pop_back();
}
}
}
//010
void upgrade2(vector<int> &res){
if(res.size()>=3){
int l=res.size();
int cnt1=res[l-1],cnt2=res[l-2],cnt3=res[l-3];
if(cnt1==0 && cnt2==1 && cnt3==0){
res.pop_back();
res.pop_back();
}
}
}
bool check(int x){
//cout<<x<<endl;
vector<int> tmp(n+1,0);
for(int i=1;i<=n;i++){
if(a[i]>=x) tmp[i]=1;
else tmp[i]=0;
}
vector<int> v;
for(int i=1;i<=n;i++){
v.push_back(tmp[i]);
upgrade1(v);
upgrade2(v);
}
int c0=0,c1=0;
for(auto son:v){
//cout<<son<<" ";
if(son==0) c0++;
else c1++;
}
//cout<<endl;
return c1>c0;
}
void solve(){
cin>>n;
a.resize(n+1,0);
for(int i=1;i<=n;i++) cin>>a[i];
int l=0,r=1e9;
while(l<r){
int mid=(l+r+1)/2;
if(check(mid)) l=mid;
else r=mid-1;
}
int ans=l;
cout<<ans<<endl;
}