Codeforces Round #570 (Div. 3) G. Candy Box (hard version)

题目链接:codeforces.com/contest/1183/problem/G

题意:现在有 n n n个糖果,每个糖果有一个种类,并且每个糖果有一个你是否喜欢的标记( 0 0 0喜欢, 1 1 1不喜欢),你需要选出数目两两互不相同的各种类的糖果(可以一个类型的糖果不选完),问糖果最多可以选择多少个,在保证糖果最大的情况下,要求标号为 1 1 1的糖果最多,输出最大的糖果数和标号为 1 1 1的糖果数。

解题心得:要糖果数目最多,可以用一个优先队列按照糖果数目多的优先,当糖果数目相同时标号1糖果数量多的优先。每次从队首取出一个类型的糖果记录取出的个数,如果队列中下一个类型糖果和当前取出的个数相同,则将下一个取出,然后将其糖果数目减一,如果取出的这个种类糖果标号 1 1 1数目和总数相同时,标号 1 1 1个数种类的糖果也 − 1 -1 1,然后重新压入队列。



#include <bits/stdc++.h>
using namespace std;
typedef complex<double> cp;
typedef long long ll;
const int maxn = 2e5+100;
const double pi = acos(-1);

struct Node {
    int sum, cnt1;

    bool operator < (const Node &x) const {//定义优先级
        if (this->sum != x.sum)
            return this->sum < x.sum;
        else
            return this->cnt1 < x.cnt1;
    }
};

int n, t;
map <int, int> maps, map1;//分别记录一个类型糖果的数量和同类型标号为1的糖果数量
priority_queue <Node> qu;

void init() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++) {
        int type, f;
        scanf("%d%d", &type, &f);
        maps[type]++;
        map1[type] += f;
    }

    map <int, int> :: iterator iter;
    for(iter=maps.begin();iter!=maps.end();iter++) {//全压入队列中
        Node now = {iter->second, map1[iter->first]};
        qu.push(now);
    }
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    scanf("%d", &t);
    while(t--) {
        init();

        int now = 0;//记录当前取出种类的糖果数量
        int ans = 0, cnt = 0;//分别记录取出的糖果的数量和标号1的糖果数量
        while (!qu.empty()) {
            int sum = qu.top().sum;
            int cnt1 = qu.top().cnt1;
            qu.pop();

            if (sum == 0) break;
            if (sum != now) {
                now = sum;
                ans += sum;
                cnt += cnt1;
            } else {
                if(sum == cnt1) {//全是1时只能取1
                    sum--;
                    cnt1--;
                } else {
                    sum--;
                }
                qu.push({sum, cnt1});
            }
        }
        while(!qu.empty()) qu.pop();
        maps.clear();
        map1.clear();
        printf("%d %d\n", ans, cnt);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值