题目大意

有N个居民点在一条直线上,每个居民点有一个x表示坐标,y表示居民点的现有居民数。现在要求将居民点的居民重新分配,每个居民点的居民最远迁移的距离为R,要求分配完之后,居民点中居民数最多的居民点的居民数最少。求出居民数最多的居民点的居民数的最少值。

题目分析

求最大最小值/最小最大值的问题,可以尝试二分法,给出边界,取边界中点作为尝试值,判断尝试值是否满足要求,根据是否满足,不断调整边界,最后得到最大最小值/最小最大值。 
    自己做的时候,只知道具体的框架,但是没有解出来,最后参考了 
居民迁移-二分+贪心 解法。 
    首先将所有居民点的原有居民数,以及居民点能够到达的最左和最右边界提取出来,用于后续的安置。则问题为:将各个居民点的居民,重新分配到各个安置点,在分配的时候,两个指针,一个指向可以安置的居民点的位置,一个指向当前被分配的居民点的位置。

实现

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<set>
#include<map>
#include<functional>
#include<algorithm>
using namespace std;
int T;
int N, R;
struct Seg{
int left;
int right;
int num;
};
Seg segs[100005];
int points[100005];
bool Cmp(const Seg& seg1, const Seg seg2){
return seg1.left < seg2.left;
} /*
这道题一开始,想的是在居民点现有这么些居民数的基础上进行迁移,这样想会很乱。换一种想法:有N个居民集体,每个集体中有 num[i]个居民,且每个
集体中的人的活动范围为 [left[i], right[i]],将这N个集体安放到N个点上。
*/
bool canSet(int max_people){
//cur_seg_index 表示当前待迁移的居民点的序号, cur_pos 表示当前的目的点的序号
int cur_seg_index = 0, cur_pos = 0;
int cur_people = segs[0].num, cur_volume = max_people;
//将各个居民集体,迁移到各个居民点中去
//cur_people 表示当前要迁移的居民点中还剩余待迁移的居民的个数, cur_volume 表示当前要迁移到的目的地的空余位置
while (true){
if (cur_people <= cur_volume){//可以将当前点待迁移的所有居民都放置到 目的点
cur_seg_index++;
if (cur_seg_index == N) //迁移完了所有带迁移的居民点,则成功
return true;
cur_volume -= cur_people; //当前目的点可以放置的居民数目减少
cur_people = segs[cur_seg_index].num; //当前待迁移点的居民数 //cur_seg_index 增加,可能导致目的点 在当前待迁移居民点的左侧 过远的位置,从而需要向右调整目的点
if (points[cur_pos] < segs[cur_seg_index].left){
while (points[cur_pos] < segs[cur_seg_index].left)
cur_pos++;
cur_volume = max_people; //同时更新 目的点可以放置的最大人数
}
}
else{
//需要向下一个目的点放置剩余的居民
cur_pos++;
if (cur_pos == N) //没有可以放置的目的点了,返回失败
return false;
//当前待迁移的居民点无法再向 目的点进行迁移,返回失败
if (points[cur_pos] > segs[cur_seg_index].right)
return false;
//当前待迁移居民点 居民数减少
cur_people -= cur_volume;
//当前目的点可以存放居民数 为max_people
cur_volume = max_people;
}
} return true;
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d %d", &N, &R);
int end = 0;
for (int i = 0; i < N; i++){
scanf("%d %d", &points[i], &segs[i].num);
segs[i].left = points[i] - R;
segs[i].right = points[i] + R;
end = max(end, segs[i].num);
}
sort(points, points + N);
sort(segs, segs + N, Cmp);
int beg = 0;
end++;
while (beg < end){
int mid = (beg + end) / 2;
if (canSet(mid)){
end = mid;
}
else
beg = mid + 1;
}
printf("%d\n", beg);
}
return 0;
}
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define N 100005
struct Point{
int x;
int y;
};
Point points[N];
//left:居民点的居民能够到达的最左边的位置, right居民点的居民能够到达的最右边的位置
//num:居民点的原有的居民数目
struct Seg{
int left, right, num;
};
Seg segs[N];
bool cmp(Point a, Point b){
return a.x < b.x;
} /*判断是否存在分配方案,使得每个居民点的居民数最大为middle
分配之前,将每个居民点(x, y) 转换为 (left, right, num),然后对(left, right, num)数组进行分配。
将(left, right, num) 将num个居民根据其所能到达的最远位置left,right ,分配到其所能到达的居民点去。 pos 表示当前需要对哪个seg(left, right, num)进行分配; num表示当前需要分配的(left, right, num)居民点的现有居民数。
从左到右,遍历所有的居民点 points,进行安置 segs.
*/ bool canSet(int n, int R, int middle){
int pos = 0, num = segs[0].num;
//pos 为当前需要分配的seg
for (int i = 0; i < n; i++){
//当前可以分配居民的安置点
int p = points[i].x; int volume = middle;
//如果当前的安置点p大于pos所能到达的最远边界,说明pos在之前没有被分配完,则此次分配失败
if (segs[pos].right < p)
return false;
int j;
//尝试将各个seg的居民,分配到 当前可以分配的安置点p
for (j = pos; j <= n; j++){
if (j == n) //都分配完
return true;
//要分配的居民点已经无法到达安置点p,则分配到下一个安置点
if (segs[j].left > p){
pos = j;
num = segs[j].num;
break;
}
int cnt;
if (j == pos)
cnt = num;
else
cnt = segs[j].num;
volume -= cnt;
if (volume < 0){//安置点无法容纳更多的居民,则分配到下一个安置点
pos = j;
num = -volume;
break;
}
}
}
return false;
} int main(){
int T, n, R, min, max;
scanf("%d", &T);
while (T--){
scanf("%d %d", &n, &R);
min = 1 << 30, max = 0;
for (int i = 0; i < n; i++){
scanf("%d %d", &points[i].x, &points[i].y);
min = min < points[i].y ? min : points[i].y;
max = max > points[i].y ? max : points[i].y;
}
//将居民点按照位置从小到大排序
sort(points, points + n, cmp);
//根据原有的居民点的居民数,以及居民所最远迁移的距离,初始化segs数组,得到每个居民点能够到达的最远边界,以及原有的居民数目
for (int i = 0; i < n; i++){
segs[i].left = points[i].x - R;
segs[i].right = points[i].x + R;
segs[i].num = points[i].y;
} while (min < max){
int mid = (min + max) / 2;
//判断是否存在分配方案,使得每个居民点最多有mid个居民
if (canSet(n, R, mid)){
max = mid;
}
else
min = mid + 1;
}
printf("%d\n", max);
}
return 0;
}

hiho_1053_居民迁移的更多相关文章

  1. hihoCoder #1053 : 居民迁移(贪心,二分搜索,google在线技术笔试模拟)

    #1053 : 居民迁移 时间限制:3000ms 单点时限:1000ms 内存限制:256MB 描述 公元2411年,人类开始在地球以外的行星建立居住点.在第1326号殖民星上,N个居住点分布在一条直 ...

  2. HihoCoder 1053 居民迁移

    居民迁移 时间限制:3000ms 单点时限:1000ms 内存限制:256MB 描述 公元2411年,人类开始在地球以外的行星建立居住点.在第1326号殖民星上,N个居住点分布在一条直线上.为了方便描 ...

  3. HihoCoder 1053 : 居民迁移 二分+贪心+双指针(好题)

    居民迁移 时间限制:3000ms 单点时限:1000ms 内存限制:256MB 描述 公元2411年,人类开始在地球以外的行星建立居住点.在第1326号殖民星上,N个居住点分布在一条直线上.为了方便描 ...

  4. HihoCode-1053-居民迁移

    解法: 一开始不会做,看到标签说是贪心加二分忽然就会了,二分是分的是人口最多居住点的人口,检查人口最多的居住点人口为mid是否可行.贪心是如果从左往右循环就尽量把人口往左迁移,如果从右往左循环就尽量把 ...

  5. 洛谷P1196 银河英雄传说[带权并查集]

    题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...

  6. codevs1540 银河英雄传说

    描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶集 ...

  7. H20的题——[noip2003]银河英雄传(并查集)

    公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶集团派宇 ...

  8. NOI2002 洛谷 P1196 银河英雄传说

    神奇的并查集问题 题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩 ...

  9. 洛谷OJ P1196 银河英雄传说(带权并查集)

    题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...

随机推荐

  1. Linux 文件操作总结

    http://blog.163.com/he_junwei/blog/static/19793764620152592737741/ ioctl?? lseek?? 文件是linux中的一个重要概念. ...

  2. ural 1106,二分图染色,DFS

    题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1106 乍一眼看上去,好像二分图匹配,哎,想不出和哪一种匹配类似,到网上查了一下,DFS染 ...

  3. Poj(1220),hash

    题目链接:http://poj.org/problem?id=1200 这个题,我真是无限MLE,RE,WA,太伤心了,还是写一下吧.题意很简单(英语很好读),最后看了一下金海峰的思路.果然,应该是我 ...

  4. 第十六章:网络IPC:套接字

    16.1.引言 上一章考查了各种Unix系统所提供的经典进程间通信(IPC)机制:管道.先进先出.消息队列.信号量以及共享内存.通过这些机制,同一台计算机上运行的进程可以相互通信.本章将考查不同计算机 ...

  5. WF3.0 CallExternalMethod使用技巧

    CallExternalMethod用于工作流向宿主进程中通信 简单的介绍一下它的使用技巧,参照网上的一个questioner源码进行了改进,因为我感觉这个源码提供的通信demo过于繁琐. 看看ser ...

  6. 华东交通大学2016年ACM“双基”程序设计竞赛 1002

    Problem Description 今天小学弟又训练完了,但是小学弟又不想看球赛,于是小学弟看马赛了.他发现马鞍是一个奇怪的东西.于是小学弟根据马鞍定义了一种马鞍数:在一个二位矩阵中,马鞍数在当前 ...

  7. 阿里巴巴Double分布式服务框架

    开发人员梁飞的blog : http://javatar.iteye.com/ 梁飞关于Double框架的专访: http://www.iteye.com/magazines/103 Double项目 ...

  8. python访问mysql将返回的表转化为json

    ## Licensed to the Apache Software Foundation (ASF) under one or more# contributor license agreement ...

  9. VM安装失败

    出现:VMware安装失败 “Failed to create the requested registry key Key:installer Error:1021" 解决: 开始--运行 ...

  10. 企业邮件服务器被列入RBL,申请撤销PBL指控

    1.登录[url]http://www.spamhaus.org/pbl/index.lasso[/url],在红框中输入企业电子邮件服务器MX记录地址: