CF1579D (1400) 贪心+优先队列

https://codeforces.com/problemset/problem/1579/D

题目大意

        有 n 个人被邀请参加一个即将召开重要会议。在会议中,任意两个人可以社交,相同的两个人可以社交任意次。

        n 个人中,每个人都有一个社交能力值,其中第 i 个人的社交能力值是一个非负整数 ai,它表示这个人可以与另一个人社交的次数。

       在一个会议中,产生的社交次数越多,这次会议就会被认为越有效。具体地,一个会议的有效程度为这次会议进行时产生的社交次数。

        给你序列 a,求出一次会议的最大有效程度,要求给出任意一种可以产生这个最大有效程度的方案。

 贪心思路

一旦我们选不出 2 个大于 0 的数,就只能结束操作。

于是一个简单的贪心思路:每次选择最大和次大,使它们都 -1。

反证法:

  • 如果不选择最大和次大而是选择其它的数 -1,那么这些被减的数一定比选择最大和次大 -1 更接近 0。

    一旦一个数字被减到 0,那么它在之后的决策中就不可以被选,而选不出 2 个大于 0 的数,就意味着必须结束选择。

    由于我们要最大化选择次数,所以我们希望能选出 2 个大于 0 的数的次数尽可能的多,也就是使得可以被选择的数的数量尽可能多。

    而选择非最大、次大的数会导致可以选择的数的数量更快的减小,故选择最大、次大两个数一定不会更劣。

    综上,每次贪心的选择最大、次大两个数是正确的。

  • 然后维护最大和次大用优先队列即可

代码实现:

 

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define pi acos(-1.0)
#define endl '\n'
#define x first
#define y second
typedef long long LL;
typedef pair<int, int> PII;

void solve()
{
    priority_queue<PII> pq;  // 优先队列值为优先
    vector<PII> vv;  // 存答案
    int n,c;
    LL ma = 0;  // 谈话次数
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> c;
        pq.push({c,i});  // 存值和下标
    }
    PII m1, m2;
    while(1)
    {
        m1 = pq.top();
        pq.pop();
        m2 = pq.top();
        pq.pop();
        if(m2.x == 0 || m1.x == 0) break;  // 当出现0即没有答案,跳出
        vv.push_back({m1.y,m2.y});  // 存答案
        ma ++;
        m1.x -= 1; m2.x -= 1;
        pq.push(m1);
        pq.push(m2);
    }
    cout << ma << endl;
    for(LL i = 0; i < vv.size(); i ++) cout << vv[i].x << " " << vv[i].y << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(0);
    int T = 1;
    cin >> T;
    while(T --)
    {
        solve();
    }
    
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

永夜天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值