【刷题】【二分搜索】(1)

模板

找出最左边的一个,最右边的一个,和长度

注意找左位置时要判断:

1>不可以是字串结尾,不然为0或为空

2>不可以不等于key

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m;
const int N=100003;
int d[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]);
    
    int x;
    while(m--)
    {
        scanf("%d",&x);
        int pos1=lower_bound(d+1,d+n+1,x)-d;
        if(pos1>n || d[pos1]!=x)
        {
            printf("-1 -1 -1
");
            continue;
        }
        int pos2=upper_bound(d+pos1+1,d+n+1,x)-d;
        
        printf("%d %d %d
",pos1,pos2-1,pos2-pos1);
    }
     
    return 0;
} 

例题1

阿弥陀佛数数游戏

N个数字(N<=500000),K(K<=500000)个问题,

每个问题询问从L到R中,到底有多少个数字是KEY值?

数据都是int可以存储的

神仙思路:

题目虽然有两个关键字,但是只求第一个关键字相等的情况,所以可以直接sort

sort以后找到相等区间,再去搞第二关键字就好

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,k;
const int N=500003;
struct node
{ 
    int v,id;
    bool operator < (const node & o) const
    { return v==o.v? id<o.id :v<o.v; }
}d[N];

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i].v ),d[i].id =i;
    sort(d+1,d+n+1);
    
    int l,r;
    node t;
    while(k--)
    {
        scanf("%d%d%d",&l,&r,&t.v );
        t.id =l;
        int pos1=lower_bound(d+1,d+n+1,t)-d;
        if(pos1>n || d[pos1].v !=t.v || d[pos1].id >r)
            printf("0
");
        else
        {
            t.id =r;
            int pos2=upper_bound(d+pos1,d+n+1,t)-d;
            printf("%d
",pos2-pos1);
        }
    }
    
    return 0;
}

例题2

晒衣服

1>贪心

众所周知,堆比sort快(除了我)

#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
priority_queue <int> q;
int n,a,b;
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    int x;
    for(int i=1;i<=n;i++)
        scanf("%d",&x),q.push(x); 
    
    int t=0;
    while(q.top() > a*t)
    {
        x=q.top() -b;
        q.pop() ;
        t++;
        q.push(x); 
    }
    printf("%d
",t);
    return 0;
}

2>二分搜索答案

二分快速确认答案范围

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int n,a,b;
const int N=500000;
int d[N+3];
bool check(int tm)
{
    int cnt=tm;
    for(int i=n;i;i--)
    {
        if(d[i]<=tm*a)
            return true;
        int t=d[i]-tm*a;
        int nd=(ceil(t/(b*1.0)));
        if(nd>cnt) return false;
        else cnt-=nd;
    }
    return true;
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    sort(d+1,d+n+1);
    
    int l=1,r=d[n]/a+1;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d
",l);
    
    return 0;
} 

例题3

穿越七彩虹

小数的二分

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
double h,ed;
double x[10],r[10];
struct node
{
    double l,r;
    bool operator < (const node & o) const
    {
        if(l==o.l) return r<o.r;
        else return l<o.l;
    } 
}d[10];
bool check(double ch)
{
    for(int i=1;i<=7;i++)
    {
        double tt=sqrt((r[i]+ch)*(r[i]+ch) -h*h);
        d[i].l =x[i]-tt;
        d[i].r =x[i]+tt;
    }
    sort(d+1,d+8);
    
    double rr=d[1].r ;
    if(d[1].l >0) return false;
    for(int i=2;i<=7;i++)
    {
        if(d[i].l >= rr) return rr>=ed;
        rr=max(d[i].r ,rr);
    }
    return rr>=ed;
}
int main()
{
    scanf("%lf%lf",&h,&ed);
    for(int i=1;i<=7;i++)
        scanf("%lf%lf",&x[i],&r[i]);
    
    double l=0,r=ed;
    int cnt=400;
    while(r-l>=0.0001 && cnt--)
    {
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    
    printf("%.2lf",l);//四舍五入输出两位小数
    return 0;
}

例题4

矩形分割

平面上有一个大矩形,其左下角坐标(0,0),右上角坐标(R,R)。大矩形内部包含一些小矩形,小矩形都平行于坐标轴且互不重叠。所有矩形的顶点都是整点。要求画一根平行于y轴的直线x=k(k是整数) ,使得这些小矩形落在直线左边的面积必须大于等于落在右边的面积,且两边面积之差最小。并且,要使得大矩形在直线左边的的面积尽可能大。注意:若直线穿过一个小矩形,将会把它切成两个部分,分属左右两侧。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int r,n;
const int R=1000003;//注意:我将面积落在偏右节点上 
long long cf[R],sum[R];//cf是i项减去i-1项//sum是前i项的面积之和 ,则sum的有效部分是[2,r],第一项面积为0 

int main()
{
    scanf("%d%d",&r,&n);
    int x,y,w,h;
    while(n--)
    {
        scanf("%d%d%d%d",&x,&y,&w,&h);
        cf[x+1]+=h,cf[x+w+1]-=h; 
    }
    
    long long nw=0;//差分序列前i项的和 
    for(int i=1;i<=r;i++)
    {
        nw+=cf[i];
        sum[i]=sum[i-1]+nw;
    }
    
    long long mid=sum[r]-(sum[r]>>1);
    
     int pos=lower_bound(sum,sum+r+1,mid)-sum;
    while(pos<r && sum[pos]==sum[pos+1]) pos++; 
    
    int pos2=upper_bound(sum+1,sum+r+1,mid)-sum;
    if(pos2 && sum[pos2-1]>=mid) pos2--; //注意:这两段其实是不一样的,第二段中的sum[pos2]虽然一定大于了mid,是最小差值,但是不一定是最后一个最小差值 
    //再加上一个向后寻找就好了 
    printf("%d
",pos);
    
    return 0;
} 

例题5

有道搜索框

//注意呀:字符串可以都看作数
//只是比大小是左对齐形式罢了
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n,q;
const int N=10003;
string s[N],ask;
int pos;
int main()
{
    cin>>n;//cin和scanf不要混用
    for(int i=0;i<n;i++)
        cin>>s[i];
    sort(s,s+n);
    n=unique(s,s+n)-s;//神仙去重 

    cin>>q; 
    while(q--)
    {
        cin>>ask;
        int pos1=lower_bound(s,s+n,ask)-s;
        
        pos=ask.length() ;
        ask[pos-1]++;
        int pos2=lower_bound(s+pos1,s+n,ask)-s;
        
        if(pos1==pos2)//神仙判ask是否为s[pos1]的前缀 
        {
            ask[pos-1]--;
            cout<<ask<<endl;
        }
        else
        {
            for(int i=0;i<8 && pos1+i<pos2;i++)
                cout<<s[pos1+i]<<" ";
            cout<<endl;
        }
    }   
    return 0;
}
原文地址:https://www.cnblogs.com/xwww666666/p/11395899.html