UVALive - 6442

题目链接:https://vjudge.net/contest/241341#problem/I

题目大意:输入t,t组样例,输入n,m,有n个圆槽,m个硬币,接下来m行代表每个硬币所在的位子,要求你移动硬币使得相邻的硬币距离相等,输出最小的最大移动步数(这里所指的是

一个硬币最大移动的步数)

个人思路:一直在找规律,后来也没有找到,应该是没有规律吧,搜题解也没有搜到有什么规律,全都是用二分来求的。。。这里怎么用二分呢?

因为数据范围是n<=1e6,并且如果通过单点最大k步移动可以是这些点两两间隔n/m,那么对于任意的k’(k’>k)也一定是可以的。所以可以二分查找需要的最小单点最大移动步数。当单点移动最大步数确定为d时如何判断d是否能使得这些点均匀分布呢?
考虑每个点的移动区间,假设第i-1个点的移动区间是[prelow,prehigh],因为第i点最多可移动d步,所以第i个点的移动区间是[data[i]-d,data[i]+d],又因为第i-1个点和第i个点需要间隔step=n/m,所以第i-1个点给第i个点的约束移动区间是[prelow+step,prehigh+step],所以第i个点的实际允许移动区间是

curlow=max(prelow+step,data[i]-d), curhigh=min(prehigh+step,data[i]+d);

只要对每个点判断这个区间是否存在即curlow<=curhigh是否成立,就能判断d是否合法了。

看代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<math.h>
#include<algorithm>
#include<set>
#include<queue>
#include<map>
typedef long long ll;
using namespace std;
const ll mod=1e9+7;
const int maxn=2e4+10;
const int maxk=100+10;
const int maxx=1e4+10;
const ll maxe=1000+10;
#define INF 0x3f3f3f3f3f3f
int a[maxn];
int n,m,step;
bool solve(int mid)
{
    int prelow,prehigh,curlow,curhigh;
    prelow=a[0]-mid;
    prehigh=a[0]+mid;
    for(int i=1;i<m;i++)
    {
        curlow=max(prelow+step,a[i]-mid);
        curhigh=min(prehigh+step,a[i]+mid);
        if(curlow>curhigh)
            return false;
        prelow=curlow;
        prehigh=curhigh;
    }
    return true;
}
int main()
{
    int t,sum=1;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i=0;i<m;i++)
        {
            cin>>a[i];
        }
        sort(a,a+m);
        int l=0,r=n;
        step=n/m;
        while(l<r)
        {
            int mid=(l+r)/2;
            if(solve(mid))
            {
                r=mid;
            }
            else
                l=mid+1;
        }
        printf("Case #%d: %d\n",sum++,r);
     //   cout<<r<<endl;
    }
}

猜你喜欢

转载自www.cnblogs.com/caijiaming/p/9386050.html