数据读取

题解:二分答案。用h[]储存磁头位置,p[]储存需要读取的位置。

         在(0,2e10)内找到一个最小值mid使得每个磁头移动mid步能够扫完所有需要读取的位置。

         寻找mid的时候,由三种情况。

               一 :此时h[i]-p[j]>mid,(即磁头到所需扫描位置大于mid时,说明此时mid比最大步数要小),那么应在(mid+1,r)范围内继续寻找。

               二:在不符合一的时候,并且p[j]<h[i],(即所需读取位置在磁头左边)则磁头有两种移动方案:

                          由于磁头与左边需要读取的位置的距离为一定值,可先计算出来k=(h[i]-p[j])

                         1:先往左扫描需要扫描的地方,再向右移。此时扫描到的最大位置=p[j]+k;

                         2: 先往右扫描所需扫描的地方,再向左移。此时扫描到的最大位置=h[i]+k/2;

                   此时两种情况取一最大值maxn即为移动mid步时磁头所能到达的最大位置。

              三:在不符合一的时候,并且p[j]>=h[i],(即所读取的位置在磁头右边)则磁头所能到达的最大位置maxn=p[j]+mid;

       然后根据磁头所能到达的最大位置更新下一个所需读取的位置。若位置j>m时,说明此时mid值能够使得磁头读取完所有需要读取的位置。用ans储存下此时的mid,继续在(l,mid-1)内查找是否有更小值能够满足条件。

#include<cstdio>
#include<iostream>
#define N 100100
#define ll long long
using namespace std;
int n,m;
ll ans;
ll h[N],p[N];
void erfen(ll l,ll r)
{
    if (l>r) return ;
    bool ff=0;
    ll mid=(l+r)>>1,maxn;
    for (int i=1,j=1;i<=n;i++)
      {
           if (h[i]-p[j]>mid) break;//若此时的磁头与此时需要扫描的位置大于mid,则最大步数应大于mid 
           if (p[j]<h[i])
             {
                   ll k=mid-(h[i]-p[j]);
                   maxn=max(p[j]+k,h[i]+(k>>1));//注意">>"的运算级比+还低,要加括号 
           }
         else maxn=h[i]+mid;
         while (p[j]<=maxn&&j<=m) j++;
         if (j>m)//此时磁头能够扫完所有需要扫描的位置 
           {
                 ff=1;
                 break;
           }
      }
    if (ff) ans=mid,erfen(l,mid-1);
    else erfen(mid+1,r);
}
int main()
{
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout); 
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) cin>>h[i];//注意在windows系统下,用scanf("%lld")读取相当于读取int类型的数 
                                     //由于noip考试是在linx系统下进行,此时用scanf读入无问题 
    for (int i=1;i<=m;i++) cin>>p[i];
    ll rr=2e10;
    erfen(0,rr);
    cout<<ans<<endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}
二分
原文地址:https://www.cnblogs.com/sjymj/p/5994217.html