在一条街上有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;
}