51nod 1163 最高的奖励 (贪心+优先队列/并查集)&& 1191 消灭兔子(贪心+优先队列维护) && 1475 建设国家 (思维枚举+优先队列)

42 篇文章 0 订阅
27 篇文章 1 订阅


基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题
 收藏
 关注
有N个任务,每个任务有一个最晚结束时间以及一个对应的奖励。在结束时间之前完成该任务,就可以获得对应的奖励。完成每一个任务所需的时间都是1个单位时间。有时候完成所有任务是不可能的,因为时间上可能会有冲突,这需要你来取舍。求能够获得的最高奖励。
Input
第1行:一个数N,表示任务的数量(2 <= N <= 50000)
第2 - N + 1行,每行2个数,中间用空格分隔,表示任务的最晚结束时间E[i]以及对应的奖励W[i]。(1 <= E[i] <= 10^9,1 <= W[i] <= 10^9)
Output
输出能够获得的最高奖励。
Input示例
7
4 20
2 60
4 70
3 40
1 30
4 50
6 10
Output示例
230
相关问题
最高的奖励 V2  
320

思路:

当有10个任务,争4个时间的时候,肯定让价值大的先完成, 所以贪心一下, 用优先队列的容量维护当前第几个时间点,先按照最晚结束时间从小到大排序,如果枚举的最晚结束时间大于优先队列的容量,说明他不比跟争抢,就直接扔进去,目前这个肯定可以完成, 如果==有限队列容量, 也就是前面已经安排满了,需要争抢了, 就像最前面说的, 肯定要价值大的在前面,优先队列的作用就出来了, 看最小的价值,跟当前枚举的价值哪个大,当前的大,就把队列的pop,这个push进去~

代码:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. #include <algorithm>  
  5. #include <cmath>  
  6. #include <queue>  
  7. using namespace std;  
  8. const int maxn = 5e4 + 5;  
  9. typedef long long ll;  
  10. struct node  
  11. {  
  12.     int e, w;  
  13. }a[maxn];  
  14. priority_queue<int, vector<int>, greater<int> > pq;  
  15. int cmp(node a, node b)  
  16. {  
  17.     return a.e < b.e;  
  18. }  
  19. int main()  
  20. {  
  21.     int n;  
  22.     ll ans = 0;  
  23.     scanf("%d", &n);  
  24.     for(int i = 1; i <= n; i++)  
  25.     {  
  26.         scanf("%d%d", &a[i].e, &a[i].w);  
  27.     }  
  28.     sort(a+1, a+1+n, cmp);  
  29.     for(int i = 1; i <= n; i++)  
  30.     {  
  31.         if(a[i].e > pq.size())  
  32.             pq.push(a[i].w);  
  33.         else  
  34.         {  
  35.             if(a[i].w > pq.top())  
  36.             {  
  37.                 ans = ans + a[i].w - pq.top();  
  38.                 pq.pop();  
  39.                 pq.push(a[i].w);  
  40.             }  
  41.         }  
  42.     }  
  43.     printf("%lld\n" , ans);  
  44.     return 0;  
  45. }  
并查集思想:

首先按照权值从大到小完成, 这样把时间用完一定是最优的~ 怎样知道时间是否用完呢? 首先遇到一个任务, 贪心的想,肯定是把他放到离他最近的可用的时间把,放前面可能把其他的挤了。我们用并查集的pre表示每个点前面可用的时间点是哪,如果是0,说明没有空间了~

这题跟题目1 题目2很像,只不过那两个没有要求每个数的pre都是最新的, 比如第一个维护区间的,他每次更改都只是更改最后的pre是最新的,前面的并没有更改,但是这题要求每个点都是最新的, 所以就要在find函数里,边找边路径压缩,前面两题就是没有路径压缩。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
const int maxn = 5e4 + 5;
typedef long long ll;
int pre[maxn];
struct node
{
    int e, w;
}a[maxn];
int Find(int x)
{
    if(x <= 0) return -1; //这里一定要注意,不能返回0,下面那个可能返回0,但是他是可以的,正好把最后一个用了。
    if(x == pre[x]) return pre[x] = x-1;  //直接返回他前面那一个,相当于这个时间点用掉了
    else return pre[x] = Find(pre[x]);  //路径压缩, 把所有他指向的都更新成-1,这是这个题目必须的, 因为他可能询问任何一个点
}
int cmp(node a, node b)
{
    return a.w > b.w;
}
int main()
{
    int n;
    ll ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d", &a[i].e, &a[i].w);
    }
    sort(a+1, a+1+n, cmp);
    for(int i = 0; i < maxn; i++)
        pre[i] = i;
    for(int i = 1; i <= n; i++)
    {
        if(a[i].e > n) ans = ans + a[i].w;
        else
        {
            int x = Find(a[i].e);
            if(x != -1)
                ans = ans + a[i].w;
        }
    }
    printf("%lld\n" , ans);
    return 0;
}

基准时间限制:1 秒 空间限制:131072 KB 分值: 40  难度:4级算法题
 收藏
 关注
有N只兔子,每只有一个血量B[i],需要用箭杀死免子。有M种不同类型的箭可以选择,每种箭对兔子的伤害值分别为D[i],价格为P[i](1 <= i <= M)。假设每种箭只能使用一次,每只免子也只能被射一次,计算要消灭地图上的所有兔子最少需要多少Q币。如不能杀死所有兔子,请输出No Solution。
特别说明:1、当箭的伤害值大于等于兔子的血量时,能将兔子杀死;2、血量B[i],箭的伤害值D[i],箭的价格P[i],均小于等于100000。
Input
第1行:两个整数N,M,中间用空格分隔(1 <= N, M <= 50000),分别表示兔子的个数和箭的种类。
第2 - N + 1行:每行1个正整数(共N行),表示兔子的血量B[i](1 <= B[i] <= 100000)。
第N + 2 - N + M + 1行:每行2个正整数(共M行),中间用空格分隔,表示箭所能造成的伤害值D[i],和需要花费的Q币P[i](1 <= D[i], P[i] <= 100000)。
Output
输出最少需要多少Q币才能消灭所有的兔子。如果不能杀死所有兔子,请输出"No Solution"。
Input示例
3 3
1
2
3
2 1
3 2
4 3
Output示例
6
李陶冶  (题目提供者)

刚拿到这题,知道跟上题差不多,但还是没想出来,感觉数据量小点, 不就是个km裸题么。。唉, 当时想着  知道一般是优先队列维护符合条件里最小的q币,当时就想到,最小的那个兔子,不一定从所有里面找一个最小的,那样大的兔子可能杀不死,知道射的剑离着兔子的血越近越好,并且找一个q币最低的。。当时想如果每只兔子找他最近q币最低的, 那这样也不一定成立,他可能用一个离着很远但是q币很小的,而且不影响结果的剑射死。。然后gg了。。

正解思路:

贪心,讲的是无后效性贪心, 每一步都是局部最优,而且无关整体。那这题,可以这样想, 把兔子血量从大到校排个序, 射的剑按照伤害值从大到小排个序, 对于每个兔子,把所有可以杀死他的箭都放到队列里,这个兔子要死,肯定要从可以杀他箭里选一个, 那肯定选最小q币那个, 不比担心前面的兔子怎样,然后前面兔子又成了血量最多的兔子。。如果队列i没有箭了,说明没有可以杀死当前兔子的箭了, 输出no;

总结:

这种优先队列/栈 只放符合条件的变量方法很常见, 每次从符合条件里 扔掉一个,然后不断补充,要会用这种方法。

再就是如果不知道是贪心,可能觉得是dp了把,许多题,感觉前面会影响到后面,用脑子想很容易乱套, 那就倒着想,这样可能就无法影响后面了,这题就是,不能从前面把符合条件的扔到队列里,只能从后面扔,细细体会下。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 5e4 + 5;
struct node
{
    int x, w;
}a[maxn];
int b[maxn];
int cmp1(node a, node b)
{
    return a.x > b.x;
}
int cmp2(int a, int b)
{
    return a > b;
}
int main()
{
    int n, m;
    while(~scanf("%d%d", &n, &m))
    {
        priority_queue<int, vector<int>, greater<int> > pq;
        for(int i = 1; i <= n; i++)
            scanf("%d" ,&b[i]);
        for(int i = 1; i <= m; i++)
            scanf("%d%d", &a[i].x, &a[i].w);
        sort(a+1, a+1+m, cmp1);
        sort(b+1, b+1+n, cmp2);
        int j = 1, flag = 0;
        ll ans = 0;
        for(int i = 1; i <= n; i++)
        {
            for(;a[j].x >= b[i] && j <= m; j++)
                pq.push(a[j].w);
            if(!pq.size())
            {
                flag = 1;
                printf("No Solution\n");
                break;
            }
            else
            {
                ans = ans + pq.top();
                pq.pop();
            }
        }
        if(!flag)
            printf("%lld\n", ans);
    }
    return 0;
}


基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题
 收藏
 关注

小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
System Message  (题目提供者)

这题为啥才3级,感觉好难啊- -

先转一下网上许多人的题解,都一样我也不知大哪个是原创QAQ:

将距离按照降序排列,然后在只有满足当前数量<i的时候往里面添加元素,弹出来的是当前队列中的人口最少的城市,队列中的是被选中的满足接下来长度要求的城市,然后在这一轮中添加这个距离的所有城市,之后如果城市数量超出,再将人口最少的城市排除。也就是说,队列里面是符合长度要求的人口最多且在当前状况下数量最多的城市。


其实就是, 在输入的时候,预处理第几层有哪些城市, 也就是H-去他的直径, 他最多到第几层, 用vec存下第几层有的城市, 枚举答案里有几个中间站(几层),然后假设这就是答案, 在把这一层的城市都加上之前, 先把队列里多的城市pop掉,肯定留下的是人口最多的城市啊, pop到i-1,然后把所有这一层的城市加进来,再pop到i+1,这时候队列里就是这一层的最优答案,更新最终答案,注意并不是层数越多, 人口一定越多, 可能之前pop人口很多的城市, 后来加进来的都是很少的,另外,程序写法也挺巧妙的~

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 1e3 + 5;
vector<int> v[maxn];
priority_queue<int, vector<int>, greater<int> > pq;
int h[maxn], p[maxn], x, n;
int main()
{
    scanf("%d%d", &n, &x);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d%d", &h[i], &p[i]);
        int t = x-h[i] > n ? n : x-h[i];
        v[t].push_back(p[i]);
    }
    int ans = 0;
    for(int i = 1, s = 0; i <= n; i++)
    {
        while(pq.size() >= i)
                s -= pq.top(), pq.pop();
        for(int j = 0; j < v[i].size(); j++)
            pq.push(v[i][j]), s += v[i][j];
        while(pq.size() > i+1)
                s -= pq.top(), pq.pop();
        ans = max(s, ans);
    }
    printf("%d\n", ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值