◆HihoCoder 1053◆
居民迁移
□题目□
↓HihoCoder 的链接↓
□解析□
·从头讲起
从样例看来输入的顺序并没有按位置排序,这极其不利于之后的计算,所以将居民点存储为一个结构体,然后按照位置从小到大sort一遍。但是因为是多组数据,就必须要给每一个数组清零,避免产生影响。
·二分查找
由于数据规模极大(1e9)而且不方便直接计算,所以很容易想到运用二分查找的方法来辅助求解,而二分查找的目标就是所得的答案。
·贪心检测
我们从左到右对每一个居民点进行检测,即从1~n。
这里就存在一个很简单的贪心思想——把当前城市的人,无论是否超过二分出的限制(以下统称x),尽可能的移动到人数未超过x的能够到达的位置最靠前的点。这句话比较绕,其实就是下面这样:
但是如果当前点能够到达的点都无法继续移动(也就是能到达范围内的点的人数都大于等于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;
}