小C现在想建设一个国家。这个国家中有一个首都,然后有若干个中间站,还有若干个城市。
现在小C想把国家建造成这样的形状:选若干(可以是0个)的中间站把他们连成一条直线,然后把首都(首都也是一个中间站)连在这一条直线的左端。然后每个点可以连一个城市,特别的是最右端的点可以连接两个城市。
现在有n个城市的规划供小C选择。但是,他们那儿的交通条件比较差,他们那儿一天是2*H个小时,每个城市里面的人每天都会去首都拿一样东西,从他们所在的城市出发,到了首都之后拿了东西就走(拿东西的时间可以忽略不计),他们要在2*H个小时之内返回他们自己的家中(从家中出发到返回家中不超过2*H小时)。
每个城市有两个属性,一个是城市的直径,另外一个是能居住的人口数目。对于第i个城市而言,这两个属性分别是hi,pi。
城市的直径的意思是离这个城市出口最远的人想要出城先要在城里行走的最少的时间。
在首都,中间站,城市之间行走要花费1小时的时间。
小C想选择一些城市然后通过若干的中间站和首都连接起来,在每个人能在2*H小时返回的条件下所有城市居住的总人口数目要最多。
样例解释:最上面的蓝点表示首都,其它的蓝点表示中间站,剩下的红圈表示选择的城市。
单组测试数据。 第一行包含两个整数n 和H (1 ≤ n ≤ 1000,1 ≤ H ≤ 1000000000),表示可供选择的城市数目和时间限制。 接下来n行,每行有两个整数hi, pi (1 ≤ hi ≤ H, 1 ≤ pi ≤ 1000),第i个城市的两个属性,即直径和能容纳人口数。
输出最多能居住的人口数目。
5 10 1 1 1 1 2 2 3 3 4 4
11
思路:利用优先队列,有两种思路,由上到下和由下到上优先。
一,由下到上:对城市按照直径 h 从小到大排序,从最后端开始处理,由于从最小直径的作为最后端不一定是最优解,例如
3 4
1 2
3 3
3 3
的最优解应该是6而不是5,因此从最小直径开始依次最为最后端处理。
用 l来表示城市处理到第几层了,按理说层数是从 1 到 最大的直径层,
但是也可以用最小直径作为最后一层的标志,那么由下到上就是 1 到 H-1层,
用优先队列Q(大的优先)存放满足题目条件的城市的人口数,对于最后端的城市 k ,把 0到 k 的所有城市入队列Q,
再取两个最大的作为最后端连接的城市,此时l=a[k].h+1,再 i 从 k+1开始遍历,对于优先队列Q的值来说,他们应该
都可以放在 第l到a[i].h-1层,因此可以从优先队列Q中取出a[i].h-l个数,此时l就处理到 a[i].h层,然后再讲所有
与a[i].h相等的城市再放入队列Q,而这时 Q中的数就满足 放在 l到a[i+1].h-1层了,如此就可以找到最优解。
二,由上到下:对城市按照直径 h 从大到小排序,从最前端开始处理,对于第一层,他的可容纳的最大城市直径 h=H-1,
那么对于城市直径 h,他到多可到达第 H-h层,即到直径h时,最多可容纳 H-h+1(最后一层有两个城市)
因此用优先队列Q(小的优先)存放满足题目条件的城市的人口数 x,由城市直径大到小遍历所有城市,
在城市 k时,h=a[k].h,把所有等于城市k的直径a[k].p的城市都放入队列 Q,(在第 k 层只需要把恰好满足的放入队列,
因为其他h<a[k].h的城市会在它所在的第 H-h 层达到最优解。)再将队列中小的出队列直到队列大小小于等于 H-h+1,
保存队列中的所有元素的最大值,再将队列大小缩小到 H-h,(即将最后一层的两个去掉一个,再去求下一层的最优解);
如此就可以找到最优解。
Code 1:
/*
思路:对城市按照直径 h 从小到大排序,从最后端开始处理,由于从最小直径的作为最后端不一定是最优解,例如
3 4
1 2
3 3
3 3
的最优解应该是6而不是5,因此从最小直径开始依次最为最后端处理。
用 l来表示城市处理到第几层了,按理说层数是从 1 到 最大的直径层,
但是也可以用最小直径作为最后一层的标志,那么由下到上就是 1 到 H-1层,
用优先队列Q(大的优先)存放满足题目条件的城市的人口数,对于最后端的城市 k ,把 0到 k 的所有城市入队列Q,
再取两个最大的作为最后端连接的城市,此时l=a[k].h+1,再 i 从 k+1开始遍历,对于优先队列Q的值来说,他们应该
都可以放在 第l到a[i].h-1层,因此可以从优先队列Q中取出a[i].h-l个数,此时l就处理到 a[i].h层,然后再讲所有
与a[i].h相等的城市再放入队列Q,而这时 Q中的数就满足 放在 l到a[i+1].h-1层了,如此就可以找到最优解。
*/
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int h;
int x;
bool operator<(const node &p)const{
return h<p.h;
}
};
const int MAX_N=1005;
int n,H;
node a[MAX_N];
int main()
{
ios::sync_with_stdio(false);
cin>>n>>H;
for(int i=0;i<n;++i)
cin>>a[i].h>>a[i].x;
sort(a,a+n);
int ans=0,sum;
for(int k=0;k<n;++k)
{
priority_queue<int> Q;
sum=0;
if(a[k].h>=H) break;
for(int i=0;i<=k;++i)
Q.push(a[i].x);
for(int i=0;i<2;++i)
if(!Q.empty()){
sum+=Q.top(); Q.pop();
}else break;
int l=a[k].h+1; //尾点后移一位
for(int i=k+1;i<n;)
{
int h=a[i].h; //l->(h-1)为可填值
if(h>H) break;
while(l<h){
if(!Q.empty()){
sum+=Q.top(); Q.pop();
l++;
}else l=h; //l到h层处理完,l到h层
}
while(a[i].h==h){
Q.push(a[i].x); ++i;
}
}
while(l<H){
if(!Q.empty()){
sum+=Q.top(); Q.pop();
}else break;
l++;
}
ans=max(ans,sum);
}
cout<<ans<<endl;
return 0;
}
Code 2:
/*
思路:对城市按照直径 h 从大到小排序,从最前端开始处理,对于第一层,他的可容纳的最大城市直径 h=H-1,
那么对于城市直径 h,他到多可到达第 H-h层,即到直径h时,最多可容纳 H-h+1(最后一层有两个城市)
因此用优先队列Q(小的优先)存放满足题目条件的城市的人口数 x,由城市直径大到小遍历所有城市,
在城市 k时,h=a[k].h,把所有等于城市k的直径a[k].p的城市都放入队列 Q,(在第 k 层只需要把恰好满足的放入队列,
因为其他h<a[k].h的城市会在它所在的第 H-h 层达到最优解。)再将队列中小的出队列直到队列大小小于等于 H-h+1,
保存队列中的所有元素的最大值,再将队列大小缩小到 H-h,(即将最后一层的两个去掉一个,再去求下一层的最优解);
如此就可以找到最优解。
*/
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int h;
int x;
bool operator<(const node &p)const{
return h>p.h;
}
};
const int MAX_N=1005;
int n,H;
node a[MAX_N];
priority_queue<int,vector<int>,greater<int> > Q;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>H;
for(int i=0;i<n;++i)
cin>>a[i].h>>a[i].x;
sort(a,a+n);
int ans=0,sum=0,k=0;
while(a[k].h>=H){
++k;
}
while(k<n){
int h=a[k].h;
while(k<n){ //把处于同一层的放进去
if(a[k].h==h){
Q.push(a[k].x); sum+=a[k].x;
}else break;
k++;
}
//此时队列里面的每个数都满足要求,那么只要把多余H-h+1的取出来即可达到最优解
while(Q.size()>H-h+1){
sum-=Q.top(); Q.pop();
}
ans=max(ans,sum);
while(Q.size()>H-h){ //把最后一层的两个中取出一个
sum-=Q.top(); Q.pop();
}
}
cout<<ans<<endl;
return 0;
}