【BIT2021程设】15.北理工的恶龙

写在前面:

本系列博客仅作为本人十一假期过于无聊的产物,对小学期的程序设计作业进行一个总结式的回顾,如果将来有BIT的学弟学妹们在百度搜思路时翻到了这一条博客,也希望它能对你产生一点帮助(当然,依经验来看,每年的题目也会有些许的不同,所以不能保证每一题都覆盖到,还请见谅)。

不过本人由于学艺不精,代码定有许多不足之处,欢迎各位一同来探讨。

同时请未来浏览这条博客的学弟学妹们注意,对于我给出完整代码的这些题,仅作帮助大家理解思路所用(当然,因为懒,所以大部分题我都只给一个伪代码)。Anyway,请勿直接复制黏贴代码,小学期的作业也是要查重的,一旦被查到代码重复会严厉扣分,最好的方法是浏览一遍代码并且掌握相关的要领后自己手打一遍,同时也要做好总结和回顾的工作,这样才能高效地提升自己的代码水平。

加油!


成绩10开启时间2021年08月31日 星期二 12:30
折扣0.8折扣时间2021年09月7日 星期二 23:00
允许迟交关闭时间2021年10月10日 星期日 23:00

Description

小张最近沉迷上一款手机游戏北理工的恶龙。在这个游戏中你通过提升攻击力击败恶龙,打败所有恶龙后你可以获得游戏的胜利。
在这款游戏中,每一条恶龙有一个难度值x_i和一个经验值y_i。游戏中的英雄具有攻击力A。游戏一开始英雄的攻击力A=0。打到一条恶龙你的攻击力需要大于等于难度值x_i。在你击败恶龙以后,你的攻击力会增加经验值y_i
当然,你有时需要额外提升你的攻击力,此时你只需氪金1元,就能增长一点攻击力。小张想知道,如果他自己决定挑战恶龙的顺序,要想击败所有恶龙至少需要氪金多少钱? 

Input

第一行一个数n(1 \leq n \leq 200000 )
接下来n行每行两个数x_i,y_i(0 \leq x_i \leq 1000000, -1000000 \leq y_i \leq 1000000 )

Output

一个整数,表示小张最少需要氪金多少钱。

Notes

直接打败第一条恶龙,此时A=1,花费0元。
直接打败第二条恶龙,此时A=2,花费0元。
氪金3元,此时A=5,打败第三条恶龙,此时A=3
最后直接打败第四条恶龙。

测试用例 1以文本方式显示
  1. 4↵
  2. 0 1↵
  3. 1 1↵
  4. 5 -2↵
  5. 2 -1↵
以文本方式显示
  1. 3↵
1秒64M

题意分析:

        有趣的贪心题。还记得之前我们怎么证明贪心策略的吗?今天他又来了。

        这个题意应该不难懂,而且这个2e5的数据规模也基本断绝了你暴力搜索的念想,所以这题一定是一个P问题,那么我们就想想如何能“贪到底”吧。

        你要是游戏打得多啊,这个肌肉记忆就应该被唤醒了——对面上单太肥了,你现在打不过,那就先去欺负对面AD嘛,多提提款,自己就有钱了,那再欺负对面的上单不就水到渠成了!

        这题也是一样的思路,假如全都是那些击杀后能增长我们攻击力的“好龙”,我们肯定是要从攻击力低的打起(还是一样,读者先试证这个贪心策略,我会在稍后给出证明)。那么现在就有两个问题,一是要不要先打“好龙”再打“坏龙”,还是混合双打?二是如果全都是“坏龙”,我们又要如何处理?

        如果你想自己寻找接下来的贪心策略,请在这里止步(当然,点进这个帖子的,估计已经挣扎这一步之后放弃了)。

        

        

        如果你不能自己找到贪心策略的话,我这里给出贪心策略,请读者试着自己先证明一下(如果仍然证明不了的话,我会把具体的证明过程放在下面供大家参考):先打正收益的“好龙”、再打负收益的“坏龙”;在“好龙”中,先打难度低的“小怪”,再打难度高的“BOSS”;在“坏龙”中,先打难度+收益(此处收益为负)更高的“人畜无害龙”,再打难度+收益更低的“坏事做尽龙”。


贪心策略的证明:

        首先,我们把龙分为好龙和坏龙之后,一定要先打好龙再打坏龙,为什么呢?因为先打好龙是没有坏处的:假如我们在打某一条好龙之前先打坏龙,那么打完坏龙我们的攻击力只会降不会增,如果降完之后还是打得过好龙,那我先打好龙也没有影响;如果降之前就已经打不过好龙,需要氪金了,那降了之后就需要氪更多金了;如果降之前打得过、降之后打不过了,那就更糟糕。总而言之言而总之,先打坏龙不会有任何好处,换句话说“与其先打坏龙自废武功,不如先打好龙攒点家底”(这句话是不是很熟悉)。

        于是我们现在把所有龙分开来了,先打好龙。那么好龙的顺序又要如何排呢?其实也很简单,先打好打的,再打难打的。假如我在某一条相对好打的之前先打了另一条难打的,有以下几种可能:要么为了打难打的龙氪了很多金,之后的攻击力已经远远溢出了打好打的龙的需要,这就属于浪费;要么本身就已经能够干碎难打的龙了,那我先打好打的也没区别。总而言之言而总之,先打好打的龙肯定没错,换句话说“与其先挑战BOSS浪费钱,不如先打点小怪攒点家底”。

        最难的一部分来了,坏龙我们要怎么处理呢?先打难的?还是先打损失小的?还是综合考虑两个因素?

        我们逐个来排除吧,如果先打难的会有什么后果?给一个极端情况,某一条龙难度为100,收益为-1e1000,而其他坏龙难度不如它,但也不会对你的攻击力有如此致命的打击,很显然,这种情况下这样的龙是要作为“收尾”最后打的,一旦打完它,就要做好从此隐退江湖不再出招的觉悟。于是,先打难的,并不合理。

        先打损失小的呢?假如有一条龙,难度10,收益-1,另一条龙难度100,收益-20,我们先打谁好呢?假如我们初始攻击力为0,先打前者再打后者,总共需要氪金10+91=101,如果先打后者再打前者需要氪金100,显然是先打后者更好。于是,先打损失小的,也不合理?

        捏麻麻的,怎么打都不行了是吧。别急,我们先回到最初的起点。

        什么叫氪金最少?考虑到我们要击败所有的龙,而所有的龙的总收益是一个定值,于是我们在打完所有的龙之后,剩余的攻击力就等于总收益+氪金量,既然总收益是定值,那么我们肯定希望我们打完所有的龙之后,剩余的攻击力越少越好,最好是打完最后一条龙就自废武功、金盆洗手、退隐江湖。

        但是我们之前也提到了,一味地把损失大的留到后面打也不合理,因为可能会有某些龙的难度非常之低,能够支持我们打完这些损失大的龙之后依然还能“白嫖”的。对,我们的关键词就是“白嫖”,我们希望我们打完某些大BOSS之后能尽可能地多“白嫖”一些,这样我们一次氪金、永久收益,岂不美哉?

        那么什么样的BOSS龙能够满足这样的条件呢?我们假定我们的攻击力为0,某条BOSS的难度为x,打完对攻击力的损耗为y,那么我们氪完金、打完龙之后剩余的攻击力就是x-y,如果这个x-y非常的大,那我们就更有机会白嫖其他小坏龙。当然,这个推理过程依然不能作为贪心策略的证明,我们还需要详细证明一下。假如我们先打这些低难度的小龙,再去挑战这个高难度、低损耗的BOSS会怎么样呢?我们首先要氪金打小龙、然后要氪金把小龙损耗的攻击力补回来、然后还要氪金打大龙。不难发现,三个氪金过程中,第一和第三个的总和和我们直接打大龙氪的金是一样的,而第二个氪金过程则是完完全全彻彻底底的浪费——能白嫖的,为什么要我氪?

        (后来补充了另一个证明的思路,我们可以把难度值小于x-y的龙和这条BOSS合并为一条巨型BOSS,这条BOSS的损耗最大,更有利于满足我们“剩余的攻击力越少越好”的这个目标,这也是一种证明思路)

        于是,对于坏龙,我们证成了一种贪心策略,即用坏龙的难度减去打完之后的损耗,这个x-y越大,我们就需要越先打,这样才能尽可能多的白嫖。


伪代码:

        贪心策略证完,这道题也没有难度了。

        读入数据,先分组,分为打完涨攻击力的“好龙”和降攻击力的“坏龙”;//思考一下,收益为0的放在哪里?

        初始化一个变量ans=0作为总氪金量;

        先对好龙按照难度值进行排序,从小到大依次打过去,过程中更新ans

        再对大龙按照难度值-损耗进行排序,从大到小依次打过去,过程中更新ans

         输出ans


贴代码:

    #include <bits/stdc++.h>
    using namespace std;  
    typedef long long ll;  
    const int INF = 0x3f3f3f;  

      
    struct cmp_p{  //对好龙进行排序
        bool operator()(pair<int,int> a, pair<int,int> b){  
            if(a.first < b.first)  
                return true;  
            else return false;  
        }  
    };  
    struct cmp_n{  //对坏龙进行排序
        bool operator()(pair<int,int> a, pair<int,int> b){  
            if(a.first + a.second > b.first + b.second)  
                return true;  
            else return false;  
        }  
    };  
      
    int main(){  
        //ifstream infile("input.txt", ios::in);  
        //ofstream outfile("output.txt", ios::out);  
      
        vector<pair<int,int>> positive;  
        vector<pair<int,int>> negative;  
      
        pair<int,int> temp;  
        int n;  
        cin >> n;  
        for(int i = 0; i < n; i++){  
            cin >> temp.first >> temp.second;  
            if(temp.second >= 0)  
                positive.push_back(temp);  
            else  
                negative.push_back(temp);  
        }  
        sort(positive.begin(), positive.end(), cmp_p());  
        sort(negative.begin(), negative.end(), cmp_n());  
        ll cur = 0;  
        ll input = 0;  
        for(int i = 0; i < positive.size(); i++){  
            if(cur < positive[i].first){  
                int dif = positive[i].first - cur;  
                cur += dif;  
                input += dif;  
            }  
            cur += positive[i].second;  
        }  
        for(int i = 0; i < negative.size(); i++){  
            if(cur < negative[i].first){  
                int dif = negative[i].first - cur;  
                cur += dif;  
                input += dif;  
            }  
            cur += negative[i].second;  
        }  
      
        cout << input << endl;  
        return 0;  
    }  

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里之码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值