题目大意

有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. Python3发送post请求,自动记住cookie

    转载自:http://www.cnblogs.com/meitian/p/4607737.html 在做登录的post请求时,需要记住cookie,否则不能访问登录后的页面. 下面是登录的代码: #c ...

  2. Java的Properties类和读取.properties文件

    一..properties文件的作用 Properties属性文件在JAVA应用程序中是经常可以看得见的,也是特别重要的一类文件.它用来配置应用程序的一些信息,不过这些信息一般都是比较少的数据,没有必 ...

  3. 【Web】简谈如何监听浏览器的关闭

    > 参考的优秀文章 beforeunload实现关闭离开的提示 想起以前做的一个小系统,一个企业内部小型的测试系统,让考生在给定时间内完成考试,如果考生中退出,那么下次进来可以利用剩余的考试时间 ...

  4. C语言中的库是什么

    在使用tc编写程序时,你或许对其中的*.lib文件产生疑问,这些lib文件有什么用途? 用C 语言编程时,通常要建立一些用户函数.如果这些函数具有通用性,一般的方法是将它们作成头文件,当需要时用“#i ...

  5. 解决This function or variable may be unsafe

    工程名字右键属性-->C/C++-->预处理器-->预处理器定义-->加入:_CRT_SECURE_NO_WARNINGS

  6. [Objective-C]__bridge,__bridge_retained和__bridge_transfer的意思,区别与使用

    使用ARC能帮我们减轻不少内存管理方面的负担,尤其是对用惯了Java的程序员来说.但是像我这种Java基础本身就不牢固,做了两年的iOS已经习惯手动管理内存的半吊子,使用ARC还是经常碰壁. 对于CF ...

  7. .Net大文件上传(转--待验证)

    几种常见的方法,本文主要内容包括:    第一部分:首先我们来说一下如何解决ASP.net中的文件上传大小限制的问题,我们知道在默认情况下ASP.NET的文件上传大小限制为2M,一般情况下,我们可以采 ...

  8. Why And When To Use Pre-Update and Pre-Insert Triggers In Oracle Forms

    Whenever we commit after entering data in Oracle Forms, many triggers fires during this event and al ...

  9. Spring整合Hibernate图文步骤

    首先建立java Project工程 点击Finish完成 添加Hibernate和Spring所需要的jar包还有Mysql连接的jar包 创建Dao层,Dao层实现,Model层,Service层 ...

  10. [HDOJ3709]Balanced Number(数位dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709 题意:求区间[L,R]内每一个数中是否存在一位,使得左边的各位数*距离=右边的各位数*距离(自己 ...