Codeforces Round #657 (Div. 2) C. Choosing flowers

枚举、前缀和、二分

题意:

有m种花,每种花数量无上限。
已知对于第i种花,第一次选收获a[i].
此后,再次选第i种花收获b[i].
现在要选n种花,请问收获的最大值是多少?
1 <= n <= 10^9 , 1 <= m <= 10^5 , 0 <= a[i],b[i] <= 10^9

分析:

这一题,乍一看很像动态规划类似背包问题。但是状态却很难表示。
因此,我并没有尝试动态规划。
下面给出我的想法:

我们直观地想想,我们应该如何拿花:

我们先对所有的a和所有的b进行一次排序:
假设排成这样(从大到小,数值相等a放在前面)
[a5,a4,a9,a1,b4,a7,b5,b6,b5,b9,a2。。。。。。]
我们很明显前四个应该选择a5,a4,a9,a1
然后到第五个b4
因为前面a4被选过了所以我们不需要考虑任何也可以选b4
并且因为b4比后面的花收获都高了,所以我们就把接下来的所有都选择为b4吧

这是很显然的!

但是倘若在前面a4并没有选到呢?

[a5,a9,a1,b4,a7,b5,a4,b6,b5,b9,a2。。。。。。]
b4 变成了第四个。
如此当我们遇到b4时就面临了分歧
1.舍弃b4向后选
2.到后面去先把a4选了,然后再回来以后都选b4

这两种分歧视具体情况没有最优的。
比如b4 = 5,a4 = 1, a7 = 4,b5 = 4(a5之前选过了) n = 5
如此我们应该选择2
而对于上面的例子,我们别的不变单单让n = 1000如此我们就一定要选择1了

这种分歧我们是无法判定的!!
因此采取贪心策略,贪心的向后选是肯定不行的!!!!!

但是通过上面的分析,我们可以观察到一个重点:
就是:
我们最后一般都会在一个b停下来,然后接下来的所有次数都选他。
尤其是在n>m,要选的数目大于种类时,这是一定发生的。

例外则是
[a1,a2,a3,a4,a6,a5,a7,b1,b4,b3,b6。。。。。] 从大到小排序
n = 6 <= m
唯有这种情况我们才可能选择雨露均沾式取法。
当然如果这样[a1,b1,a3,a4,a6,a5,a7,b2,b4,b3,b6。。。。。]
的话仍然不会发生

如此,我们找到了什么?
枚举点!!!!!
我们枚举每一个b点,计算以他作为最后选择的所得到的收获。
如果n<=m,我们再将前n大的a相加进行一次比较。
最后选出最大值
如此,便可以了!!!!!!

那么重点便是枚举一个bi点,如何计算以他作为最后选择的收获?
根据我们之前的分析,我要以bi为最后选择,那么之前比bi大的a我肯定都选了 假设共x个
然后,要选择b,我需要先判断ai是否已经被选。
如果已经被选,那么接下来我就选择n-x个bi就好了
如果没有被选,那么我们先去选ai,然后再选n-x-1个bi

有了思路接下来就是编程的问题了,但是变成真的是个问题!!!

我打算先对a进行排序,从大到小
然后计算出其前缀和sum
如果n<=m的话,我们初始ans = sum[n]
再枚举bi时,我使用二分算法,找到bi在数组中的位置,也就是有多少个a比bi大
然后利用前缀和sum直接得出x个a的值
之后判断ai是否已经被选?这该如何判断?
其实只要判断对于每一朵花是a是否大于等于b就好了
因为若a>=b那么二分查找后a肯定在b的前面,也就是已经被选了!!
最后在于ans比较,留下大的
如此,我们成功全部解决了!

还需要的是,上述的需要的二分查找,因为我们是在一个递减序列中找第一个小于目标的元素
所以C++中自带的不是太好用,我重新写了一个二分查找。需要对二分查找模板足够的熟悉!!!!!

其实是很麻烦的。。。。。。

代码如下:

#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int max_m = 1e5 + 100;
pii a[max_m];
pii b[max_m];
int c[max_m];
ll sum[max_m];
bool jud[max_m];
ll n, m;

int bound(int l, int r, pii target) {
    while (l < r) 
    {
        int mid = (l + r) >> 1;
        if (a[mid] > target)l = mid + 1;
        else r = mid;
    }
    return r - 1;
}

int main() {
    ios::sync_with_stdio(0);
    int t;cin >> t;
    while (t--) {
        cin >> n >> m;
        fill(jud + 1, jud + 1 + m, false);
        for (int i = 1;i <= m;i++)
        {
            cin >> a[i].first;
            a[i].second = i;
            cin >> b[i].first;
            b[i].second = i;
            c[i] = a[i].first;
            if (a[i].first >= b[i].first)
                jud[i] = true;
        }
        sort(a + 1, a + 1 + m, greater<pii>());
        for (int i = 1;i <= m;i++)
        {
            sum[i] = sum[i - 1] + a[i].first;
        }

        ll ans = 0;
        if (n <= m)ans = sum[n];

        for (int i = 1;i <= m;i++)
        {
            ll comp = 0;
            int id = bound(1, 1 + m, b[i]);
            if (id >= n)continue;
            comp = sum[id];
            if (jud[b[i].second])
            {
                comp += b[i].first * ((ll)n - id);
            }
            el***p += c[b[i].second] + ((ll)n - id - 1) * b[i].first;
            }
            ans = max(ans, comp);
        }
        cout << ans << endl;
    }
}

注意!
有一行代码,不可或缺!

            int id = bound(1, 1 + m, b[i]);
            if (id >= n)continue;//!!!!!!!!!!

因为我们一共就要取n个,而在这里比bi大的a就已经大于等于n个了,所以我们是不可能取到bi的。在有的题目当中即使不注意这些鸡毛蒜皮的事情也无关紧要,但这里不一样。

            ll comp = 0;
            int id = bound(1, 1 + m, b[i]);
            if (id >= n)continue;
            comp = sum[id];
            if (jud[b[i].second])
            {
                comp += b[i].first * ((ll)n - id); //!!!!!!!1
            }
            el***p += c[b[i].second] + ((ll)n - id - 1) * b[i].first;//!!!!!!
            }

我们算入了sum[id],id>=n,虽然后面的n-id是负的,但是无法保证他们加起来会小于ans,不给ans造成影响。

因此,如果你少了这句话,你将在第五个测试用例报错!!!!
滑稽。。。。。。

全部评论

相关推荐

点赞 评论 收藏
分享
努力成为C语言高手:质疑大祥老师,理解大祥老师,成为大祥老师
点赞 评论 收藏
分享
Yushuu:你的确很厉害,但是有一个小问题:谁问你了?我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了😆
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务