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

思路:

这道题卡了我两天了,终于在年三十过掉了这题。一开始思路是对的,就是优先队列,但是很多细节处理的不好,需要注意的点很多。
题目说的很啰嗦,要细细理解。之所以时限给的是2*H,是因为考虑到往返程,所以计算路径长度的时候只需要考虑H就可以了。图片中的线段通过的时间都是1,通过中间站不需要时间,每个城市通过的时间都给出来了。能构造出的树的形状已经确定了,那么我们需要考虑的就是树的深度,以及城市位置的安排。
其实把题目条件变换一下,利用总时限H减掉每个城市自己的时限,就得到这个城市到首都的路上所需要的最大时限。很显然,这个路上的时限必须要大于0(这点很关键,我没考虑这个WA了很久),也就是说,这个问题可以化简成,每个任务都有自己的时限和价值,在不超过所选择任务时限的前提下,每个单位时间只能完成一个任务,能获得的最大值是多少。但是题目还加了一点东西,那就是最后一个中转站可以连接两个城市,也就是说最后一个单位时间可以完成两个任务。
按照贪心的思路,时限早的任务要先完成,因为时限晚的活动空间就很大,也可以替代之前的任务;同一时限的情况下显然价值越大的任务越先完成。这样先给所有的城市按照之前所说排个序,然后从头到尾扫一遍。同时保存一个当前时间的值now,初始化为1,设一个当前最优值res,最终答案ans,对于当前的这个城市,时限为t,价值为v,每次添加一个城市就++now,如果当前t >=now,那么说明这个城市可以接着尾巴添加一个中转站,并添加一个城市,此时now++,另外当 t == now - 1 的情况,这说明当前的城市和之前末尾的城市是一个时限t,这里处理很重要,首先不管怎样,先假设当前时间就作为末尾端,所以虽然时限now超过了t,但是当前城市还是可以插在末尾,并且更新ans;然后就要判断这个城市是否比之前已经放过的最小的城市的价值大,如果大的话就可以直接替换,因为当前城市的时限到目前为止是最大的,它可以替换之前任何一个城市,使得结果更优。这样这里就需要用一个优先队列来保存之前所填充的城市价值,实时维护最优值。
剩下的就是码代码了,代码不长但是我实力较弱,感觉也不是很好写,逻辑上有点疏漏就会出错,WA了7发终于过了,爽!
做完题就应该该好好过年了,前途有量,无需多想,明年加油!

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1005;

struct node {
    int t, v;
    bool operator < (const node &b) const {
        if (t == b.t) return v > b.v;
        return t < b.t;
    }
}a[MAXN];

int main() {
    //freopen("in.txt", "r", stdin);
    int n, h, Max = 1;
    scanf("%d%d", &n, &h);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &a[i].t, &a[i].v);
        a[i].t = h - a[i].t;
        Max = max(Max, a[i].t);
    }
    sort (a + 1, a + 1 + n);
    priority_queue <int, vector <int>, greater<int> > que;
    int ans = 0, now = 1, res = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i].t == 0) continue;
        if (a[i].t == now - 1) {
            ans = max(ans, res + a[i].v);   // 这里假设最后连接两个城市的情况,直接更新ans
            if (!que.empty() && a[i].v > que.top()) {
                res += a[i].v - que.top();      // 这里用当前城市来替代之前已经安排的城市,使最优值更优
                que.pop(); que.push(a[i].v);
            }
        }
        else {
            ++now;
            que.push(a[i].v);      // 时限符合条件,直接把当前城市安排到最后
            res += a[i].v;
        }
        ans = max(ans, res);
    }
    printf("%d\n", ans);
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值