【二分查找&贪心】 ◆HihoCoder 1053◆ 居民迁移

◆HihoCoder 1053◆

居民迁移


□题目□

↓HihoCoder 的链接↓


□解析□

·从头讲起

从样例看来输入的顺序并没有按位置排序,这极其不利于之后的计算,所以将居民点存储为一个结构体,然后按照位置从小到大sort一遍。但是因为是多组数据,就必须要给每一个数组清零,避免产生影响。

·二分查找

由于数据规模极大(1e9)而且不方便直接计算,所以很容易想到运用二分查找的方法来辅助求解,而二分查找的目标就是所得的答案。

·贪心检测

我们从左到右对每一个居民点进行检测,即从1~n。

这里就存在一个很简单的贪心思想——把当前城市的人,无论是否超过二分出的限制(以下统称x),尽可能的移动到人数未超过x的能够到达的位置最靠前的点。这句话比较绕,其实就是下面这样:

解析1

但是如果当前点能够到达的点都无法继续移动(也就是能到达范围内的点的人数都大于等于x了),而且当前点人数仍然大于x,就说明当前方案不可行。

它的证明比较简单——假设当遍历到点 i 的时候,它无法访问到点 i-1 能够访问到的最靠前的点 f 了,但是点 f 的人数小于x且点 i 能够访问到点 i-1, 则点 i 能够访问到最近的点 g 也一定能够访问到点 f ,所以若点 g 不将点 f 填满,则点 g 的人数一定会更多,也就是点 i 能够移动到点 g 的人数一定更少,同理,点 i 能够转移的人数就会变少,可能会超出x,则不符合最优解。就像下面:

这里写图片描述

由于一个人不能移动两次,则需要存两个值——tot[i][0] 表示点i有多少人可以移动(原住民);tot[i][1] 表示点i有多少人不能移动(搬迁后)。

为了节省时间,我们可以存两个指针——i:当前准备移动点i的人;full:现在能够到达的点中没有满的最靠前的点(其实通过之前的贪心策略,我们可以知道——full之后的点一定是没有满的)。当full或者i已经遍历完整个数组了,则判断所有的tot[][0],如果还存在一个 tot[][0] 不为0,则返回错误(因为tot[][1]一定是满的)。


□代码片□

看可以,但是别copy啊……(⊙_⊙)

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100000
int n,r;
struct CITY
{
    int pos,tot;
}c[MAXN+5];
bool cmp(CITY A,CITY B){return A.pos<B.pos;}
int tot[MAXN+5][2],full;
bool Check(int x)
{
    memset(tot,0,sizeof tot);
    full=1;
    int i=1;
    tot[1][0]=c[1].tot;
    while(i<=n && full<=n)
        if(abs(c[i].pos-c[full].pos)>r)
            if(full>i) return false;
            else full++;
        else
            if(tot[full][1]+tot[i][0]<=x)
                tot[full][1]+=tot[i][0],tot[i][0]=0,i++,tot[i][0]=c[i].tot;
            else
                tot[i][0]-=x-tot[full][1],tot[full][1]=x,full++;
    for(int j=1;j<=n;j++)
        if(tot[j][0])
            return false;
    return true;
}
int Solve(int l,int r)
{
    while(l+1<r)
    {
        int mid=(l+r)/2;
        if(Check(mid))
            r=mid;
        else
            l=mid;
    }
    return r;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int MAX=0;
        scanf("%d%d",&n,&r);
        for(int i=1;i<=n;i++)
            scanf("%d%d",&c[i].pos,&c[i].tot),MAX=max(MAX,c[i].tot);
        sort(c+1,c+1+n,cmp);
        int res=Solve(0,MAX);
        printf("%d\n",res);
        for(int i=1;i<=n;i++)
            c[i].pos=c[i].tot=0;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值