原题连接:http://codeforces.com/contest/344/problem/E
题意:
一条水平的磁道上有n个磁头和m个待扫描的点,磁头可以左右互不干扰的移动去扫描点,每秒移动一个单位,求最少要花多少时间;
解法(贪心+二分查找):
从整个磁道的最左端的待扫描的点开始,如果该区域点左端没有磁头,那么一定是离该点右端的第一个磁头去扫描它,找到一个最小需要的时间len, 在保证可以扫到最左端的第一个能被扫到的情况下,该磁头能向右走多远就走多远,这样就可以扫到更多的点(贪心)。如果该点左边有磁头,那么先判断该磁头可否扫到改点,不行再用右边的磁头去扫,这样可以保证右边的磁头向右走的比较远,然后找到一个最小的时间len,进行二分搜索判断,就可以以了
下面是codeforces上面的题解:
大意为:先确定一个时间t进行二分查找,如果第一个磁头h[0]>最左端的待扫描点p[0],那么直接向右边走ts,否则的话会存在两种情况,在时间t内,先向右走,在折返回到左边,或者是先向左走,利用剩余的时间,向右走(向右越远,扫描的磁头越多),比较哪种可以向右边走的比较远,选哪种,(最左端那个一定需要被该磁头扫的),然后会出现图片的那几条公式判断两种走法的距离,然后不断的更新最左端的待扫点直到最后看所有磁头扫完的时候,判断所有扫描点是否被扫完,就能判断在t时间内能不能完成扫描。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
const __int64 maxn=100050;
__int64 h[maxn],p[maxn],vis[maxn];
int main()
{
__int64 n,m,i,j,cj,len,mid,l,r,dest;
scanf("%I64d%I64d",&n,&m);
for(i=0;i<n;i++)
scanf("%I64d",&h[i]);
for(i=0;i<m;i++)
scanf("%I64d",&p[i]);
//先随机取一个磁头,确定该磁头扫描整个磁道要花的时间,以此为最少时间
if(h[0]<p[0])
len=p[m-1]-h[0];
else
{
len=h[0]-p[0];
if(h[0]<p[m-1])
len=min(len*2+p[m-1]-h[0],len+2*(p[m-1]-h[0]));
}
l=0,r=len;
int flag;
while(l<r)
{
mid=(l+r)>>1;
memset(vis,0,sizeof(vis));
for(i=cj=0;i<n;i++)//按照磁头从左到右的顺序,把磁头可以扫描的点扫描记录
{
if(h[i]<=p[cj])//如果当前最左边未被扫描的点在当前的磁头的右边
dest=h[i]+mid;
else//在左边
{
if(mid<(h[i]-p[cj]))
break;//如果该磁头无法到达左边没有扫描过的点,直接跳出
dest=max(p[cj]+mid-(h[i]-p[cj]),h[i]+(mid-(h[i]-p[cj]))/2); //先向左走和先向右走,看哪种走的右边比较远
}
for(j=cj;p[j]<=dest && j<m ;j++)//将扫描过的点记录
vis[j]=1;
if(vis[m-1])//全部区域扫描完成,结束
break;
cj=j;//还没扫描完,记录下当前最左边未被扫描的点位置
}
if(vis[m-1])//判断是否扫描完毕,继续二分搜索
r=mid;
else
l=mid+1;
}
printf("%I64d\n",l);
return 0;
}