二分大法示例-cf的Enduring Exodus

来源:互联网 发布:单片机助手 编辑:程序博客网 时间:2024/06/06 10:55

在一条街上有n个房子,其中有一些已经住了人有一些没住人,现在你和你的k个小伙伴们来这里找房子住,你们可以随便找一些空着的房子住,当你和你的小伙伴们(共k+1人)住进去以后,离你最远的那个小伙伴的距离是x,现在问题是,这个x最小能是多少

Input
第一行是2个整数n 和k (1 ≤ k < n ≤ 100 000) — 这条街上房子的个数n和你有k个小伙伴.

第二行是一个长度为n的01串,第 i个字符如果是1说明这里已经有人住了,如果是0表示你们可以住这间屋子。保证至少有 k + 1 个 ‘0’, 所以你们肯定是能都住下的。

看了这道题,首先想到的就是 枚举起点+二分终点 即枚举每一个左端点,之后二分右端点。

还要知道一个点就是要达到离自己最远的伙伴的最小距离,就要使自己越往中间靠

直接上代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>using namespace std;#define N 100010 //宏定义N范围int room[N];char s[N];int main(){    int i,j,k,l;    int a,c,b,n,m;    while(~scanf("%d %d",&n,&k))    {        scanf("%s",&s);        room[0]=0;         for(i=1;i<=n;i++)        {            m=s[i-1]-'0';            if(m)                room[i]=room[i-1];            else                room[i]=room[i-1]+1;        }//利用room记录到第i个为止共有几个空位        int  le,ri,mark,mid,pos;        int ans=10e6; // ans用于记录最远伙伴的最小距离,预设10e6        for(i=1;i<=n;i++)        {            le=i;ri=n;//定义左右端点,最开始左端点有for循环枚举            mark=-1;  //右端点再每个枚举情况里 二分查找右端点                       //mark标记记录右端点的位置            if(room[i]==room[i-1])                  continue;//如果第i和第i-1值一样,说明第i个没有空位,跳过              while(le<=ri)//二分查找,当ri>le时跳出            {                mid=(le+ri)/2; //刷新中值                if(room[mid]-room[i]>=k)//如果第mid的值减第i个值大于等于                {                       //k,说明要找的右端点再le~mid之间                    ri=mid-1;   //更新边界,右端点往zuo靠拢                    mark=mid;   //记录此时右端点值                }               //其实ri=mid-1 ,le=mid+1 是为了跳出循环                else            //真正记录每一次右端点的是mid即mark                    le=mid+1;mark//反之,说明要找的右端点再mid~ri之间            }            if(mark!=-1)            {                l=mark-i+1;//住下全部伙伴的房子长度                int p1,p2;//定义p1,p2为长度最靠中间的位置,因为要达到最远伙伴                if(l%2==0)//的最小距离,即要越往中间靠                {                    p1=(mark+i)/2;                    p2=p1+1;                    }                else                    p1=p2=(mark+i)/2;                while(1)//即定义p1,p2的两个最中间的位置往两个方向寻找,其中一个                {                           if(s[p1-1]=='0')                    {                        pos=p1;                        break;//找到空位就停止搜索,该值即位自己站的位置                    }                    else if(s[p2-1]=='0')                    {                        pos=p2;                        break;//找到空位就停止搜索,该值即位自己站的位置                    }                    p1--;p2++;往两边搜索                }                ans=min(ans,max(pos-i,mark-pos));//更新最小距离            }        }        printf("%d\n",ans);    }    return 0;}
原创粉丝点击