【问题背景】

zhx 为他的妹子制造了一个特别的图灵机(的磁带) 。

【问题描述】

有一个两端无限长的磁带,有N 个读取指针在某些位置,需要读取 M 个位置上的数据。每一个单位时间每个读取指针都可以向左或向右移动一格。如果存在某个时刻某个读取指针在某一个位置上(包括0时刻),那么这个位置上

的数据就会被读取。(注意,不需要读取的位置也能被指针经过)

zhx 去陪妹子了,现在你需要帮他解决一个问题,最少经过多少时间,M个需要读取的位置都能被读取?

【输入格式】

第一行两个整数N,M,表示指针数和需要读取的位置数。

接下来一行N 个整数,a " 表示第 i 个指针初始位置。

接下来一行 M 个整数,b " 表示第 i 个需要读取的位置。

【输出格式】

一行一个整数,代表需要的最小时间。

【样例输入】

3 4

2 5 6

1 3 6 8

【样例输出】

2

【样例输入】

3 3

1 2 3

1 2 3

【样例输出】

0

分析:
  正解没想出来,直接打的暴力50分,TLE
  正解竟然是二分,万万没想到。想来也满足二分答案的性质,二分出答案后,接下来是  判断答案是否行得通:采用贪心的思想,用靠前的指针尽量指向靠前的目标。 
代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#define N 100010
#define ll long long
#define INF 10000000000000LL
using namespace std;
ll a[N],b[N],belong[N],n,m,ans=INF;
ll read()
{
    ll num=0,flag=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
    while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
    return num*flag;
}
bool check(ll mid)
{
    for(ll i=1,j=1;i<=n;i++)
    {
        if(a[i]-b[j]>mid)return false;
        ll reach;
        if(b[j]<a[i])
        {
            ll rest=mid-(a[i]-b[j]);
            reach=max(b[j]+rest,a[i]+rest/2);
        }
        else reach=a[i]+mid;
        while(b[j]<=reach&&j<=m)j++;
        if(j>m)return true;
    }
    return false;
}
int main()
{
    //freopen("jh.in","r",stdin);
    freopen("read.in","r",stdin);
    freopen("read.out","w",stdout);
    n=read();m=read();
    for(ll i=1;i<=n;i++)a[i]=read();
    for(ll j=1;j<=m;j++)b[j]=read();
    ll l=0,r=INF,ans;
    while(l<=r)
    {
        ll mid=(l+r)/2;
        if(check(mid))
        {
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/harden/p/5962175.html