A - 居民迁移
公元2411年,人类开始在地球以外的行星建立居住点。在第1326号殖民星上,N个居住点分布在一条直线上。为了方便描述,我们设第i个居住点的位置是Xi,其中居住着Yi位居民。随着冬季的到来,一些人口较多的居住点的生态循环系统已经开始超负荷运转。为了顺利度过严冬,殖民星上的居民一致同意通过转移到人口较少的居住点来减轻人口众多的居住点的负荷。
遗憾的是,1326殖民星的环境非常恶劣。在冬季到来前,每个居民点的居民最远能迁移到距离不超过R的居民点。1326殖民星的居民希望知道,如何安排迁移才能使完成迁移后人口最多的居民点人口最少?
注意有可能存在多个居民点位置相同。
Input
第一行包含一个整数T(1 <= T <= 10),代表测试数据的组数。
每组数据的第一行包含2个整数N(1 <= N <= 100000)和R(0 <= R <= 10^9)。
以下N行每行包含两个整数,Xi和Yi(0 <= Xi, Yi, <= 10^9)。
Output
对于每组数据输出迁移后人口最多的居民点人口最少可能的数目。
Sample Input
3
5 1
10 80
20 20
30 100
40 30
50 10
5 10
10 80
20 20
30 100
40 30
50 10
5 20
10 80
50 10
20 20
30 100
40 30
Sample Output
100
50
48
【解析】
初看这道题,不难发现,它是一道二分题。但是二分往往伴随着判断,这道题又应该怎么判断呢?我的思路是:先将原数组的数值复制在另一个a数组里,后将a数组的值与mid(迁移后人口最多的居民点人口最少可能的数目)相减,后再一个一个点的进行枚举(双重【一重点,一重它能到达的位置】),如果相加小于0,就加;反之return 0.但是后来才想到,万一别的地点又要移动呢?移动后,加上又可行了呢?所以这样做就很麻烦。于是,第二天,题解来了。判断思路主要是如下:
首先我们开辟两个新的数组,一个b用于存放原始数组的值,一个c用于移动(初值为0)。然后用p,q来判断,有如下情况:
if( abs(a[p].X - a[q].X) > R ) {//如果超出距离
if( p > q ) {//但是所需移动的点在存放点前,就更换存放点
q++;
}
else if( p < q ) {//但是如果所需移动的点在存放点后,如果放又超出距离,那么便无法再放置。
return false;
}
}
else if( b[p] + c[q] > x ) {//将b往c移动(当正的大于负的)
b[p] = b[p] - (x-c[q]);//(x-c[q])剩下的空间
c[q] = c[q] + (x-c[q]);
q++;//c已满
}
else if( b[p] + c[q] <= x ) {//负的大于正的,将b往c移动
c[q] = c[q] + b[p];
b[p] = b[p] - b[p];
p++;
b[p] = a[p].Y;
}
了解了这么多这道题基本上也就做出来了。完整代码如下:
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int INF = int(1E9);
struct node{
int X, Y;
}a[MAXN + 5];
bool cmp(node a, node b) {
return a.X < b.X;
}
int N, R, b[MAXN + 5], c[MAXN + 5];
bool Check(int x) {
memset(c, 0, sizeof c);
int p = 1, q = 1;//两个点
b[p] = a[p].Y;
while( p <= N && q <= N ) {//p后q前
if( abs(a[p].X - a[q].X) > R ) {
if( p > q ) {
q++;
}
else if( p < q ) {
return false;
}
}
else if( b[p] + c[q] > x ) {//将b往c移动(当正的大于负的)
b[p] = b[p] - (x-c[q]);//(x-c[q])剩下的空间
c[q] = c[q] + (x-c[q]);
q++;//c已满
}
else if( b[p] + c[q] <= x ) {//负的大于正的,将b往c移动
c[q] = c[q] + b[p];
b[p] = b[p] - b[p];
p++;
b[p] = a[p].Y;
}
}
for(int i=1;i<=N;i++)
if( b[i] ) return false;
return true;
}
void solve() {
int le=INF, ri=0;
scanf("%d%d", &N, &R);
for(int i=1;i<=N;i++) {
scanf("%d%d", &a[i].X, &a[i].Y);
ri = max(ri, a[i].Y);
le = min(le, a[i].Y);
}
sort(a+1, a+N+1, cmp);
if( N == 1 ) {
printf("%d\n", a[1].Y);
return ;
}
while( le < ri ) {
int Mid = (le + ri) / 2;
if( Check(Mid) ) ri = Mid;
else le = Mid + 1;
}
printf("%d\n", ri);
}
int main() {
int T;
scanf("%d", &T);
for(int i=1;i<=T;i++)
solve();
}