51Nod 1475 建设国家

小C现在想建设一个国家。这个国家中有一个首都,然后有若干个中间站,还有若干个城市。
现在小C想把国家建造成这样的形状:选若干(可以是0个)的中间站把他们连成一条直线,然后把首都(首都也是一个中间站)连在这一条直线的左端。然后每个点可以连一个城市,特别的是最右端的点可以连接两个城市。
现在有n个城市的规划供小C选择。但是,他们那儿的交通条件比较差,他们那儿一天是2*H个小时,每个城市里面的人每天都会去首都拿一样东西,从他们所在的城市出发,到了首都之后拿了东西就走(拿东西的时间可以忽略不计),他们要在2*H个小时之内返回他们自己的家中(从家中出发到返回家中不超过2*H小时)。
每个城市有两个属性,一个是城市的直径,另外一个是能居住的人口数目。对于第i个城市而言,这两个属性分别是hi,pi。
城市的直径的意思是离这个城市出口最远的人想要出城先要在城里行走的最少的时间。
在首都,中间站,城市之间行走要花费1小时的时间。
小C想选择一些城市然后通过若干的中间站和首都连接起来,在每个人能在2*H小时返回的条件下所有城市居住的总人口数目要最多。
样例解释:最上面的蓝点表示首都,其它的蓝点表示中间站,剩下的红圈表示选择的城市。
这里写图片描述
Input
单组测试数据。
第一行包含两个整数n 和H (1 ≤ n ≤ 1000,1 ≤ H ≤ 1000000000),表示可供选择的城市数目和时间限制。
接下来n行,每行有两个整数hi, pi (1 ≤ hi ≤ H, 1 ≤ pi ≤ 1000),第i个城市的两个属性,即直径和能容纳人口数。
Output
输出最多能居住的人口数目。
Input示例
5 10
1 1
1 1
2 2
3 3
4 4
Output示例
11

思路:这题的需要很强的思维能力才能想出。考虑每层能建造的城市有哪些,显然最多只能建n层。用一个vector保存每层能建造的城市,然后枚举层数,用小顶堆维护当前符合距离的最优的城市人数,对于第i层,首先把小顶堆弹到只剩下i-1个元素,表示前i-1层的最优解,然后第i层的所有城市加入小顶堆里,最后,把小顶堆弹到只剩下i+1个元素(最后一层能建造两个城市),更新当前的最优值即可。

#include <bits/stdc++.h>
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int mod = 998244353;
const double eps=1e-8;
const double Pi=acos(-1.0);
const int N= 1005;

struct Node
{
    int h,p;
};
priority_queue<int,vector<int>,greater<int> > q;
vector<int> v[N];
Node node[N];
bool cmp1(Node a,Node b)
{
    return a.h>b.h;
}
void debug(int n)
{
    for(int i=0; i<n; i++)
        printf("%d %d\n",node[i].h,node[i].p);
}
int main()
{
    //FIN;
    //FOUT;
    int n,h;
    scanf("%d %d",&n,&h);
    int cen;
    for(int i=0; i<n; i++)
    {
        scanf("%d %d",&node[i].h,&node[i].p);
        cen=(h-node[i].h)>n?n:(h-node[i].h);
        v[cen].push_back(node[i].p);
    }
    int ans=-1,tp=0;
    for(int i=1;i<=n;i++)
    {
        while((int)q.size()>=i)
        {
            tp-=q.top();
            q.pop();
        }
        for(int j=0;j<(int)v[i].size();j++)
        {
            q.push(v[i][j]);
            tp+=v[i][j];
        }
        while((int)q.size()>i+1)
        {
            tp-=q.top();
            q.pop();
        }
        ans=max(ans,tp);
    }
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值