二分大法示例-cf的Enduring Exodus

在一条街上有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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值