CF999F 纸牌与欢乐

1 CF999F 纸牌与欢乐

2 题目描述

\(𝑛\) 个玩家坐在牌桌旁。每个玩家都有一个最喜欢的号码。\(𝑗\) 号选手最喜欢的号码是 \(𝑓_𝑗\)
桌子上有 \(𝑘⋅𝑛\) 张纸牌。每张纸牌包含一个整数:第 \(𝑖\) 张纸牌包含数字 \(𝑐_𝑖\)。另外,给你提供一个序列\(ℎ_1,ℎ_2,...,ℎ_𝑘\)。它的含义如下。
玩家必须合理的方式分发纸牌,使得每个人都正好拿到 \(𝑘\) 张卡片。在所有的纸牌分发之后,每个玩家都计算自己最喜欢的数字纸牌数量。如果玩家持有包含他最喜欢的数字的 \(𝑡\) 张纸牌,玩家的欢乐数字是 \(ℎ_𝑡\)。如果一个玩家没有得到他最喜欢的数字(即 \(𝑡=0\))的纸牌,那么他的欢乐水平是 \(0\)
在分发完纸牌后,输出玩家最大可能的总快乐水平。请注意,序列 \(ℎ1,...,ℎ𝑘\) 对所有玩家都是相同的。

3 题解

容易看出,这道题需要 \(dp\) 来计算:不同的 \(h\) 值会导致贪心算出的不一定是最优解。

我们分析题目,发现 \(f\) 数组的顺序与答案无关,因此我们可以把 \(f\) 数组先进行排序。然后我们发现第二个与答案无关的值:没有人喜欢的数。由于给出的数数量一直为 \(n*k\),所以最后剩下这些数把每个人的份量全部补齐即可。题目就换了个意思出现在了我们眼前:给出一串数和一些喜欢这些数中某个数的人,每个人最多能拿到 \(k\) 个数,每个人拿到 \(x\) 个喜欢的数会产生 \(h_x\) 的价值,问价值最大值是多少。由于每个人只喜欢某一个数,我们可以将 \(f\) 数组去重并记录:有多少人喜欢哪个数。并且在 \(c\) 数组也进行类似操作,计算某个数出现的次数。这样,假设一共有 \(y\) 种不同的被喜欢的数的值,问题就进一步转化为:进行 \(y\) 次询问,每次给出一个数的数量与喜欢这个数的人,每个人最大可以拿 \(k\) 个数,每个人拿到 \(x\) 个喜欢的数会产生 \(h_x\) 的价值,求最大价值。

如此,我们就可以想到 \(dp\) 的状态为:\(dp_{i,j}\) 表示前 \(i\) 个人拿了前 \(j\) 个该数。方程为:\(dp_{i, j} = max\{dp_{i-1, j - l} + h_l\}(l \in [0, min(j, k)])\)。转移方程的大概意思为:当前这个人可以选择不拿数,也可以选择拿 \(1 - min(j,k)\) 个数,最终算出产生的最大价值。

4 代码(空格警告):

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
int n, k, cnt, ans;
int c[N*10], f[N], h[10];
int num[N], peo[N];
int dp[N][N*10];
int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n*k; i++) cin >> c[i];
    for (int i = 1; i <= n; i++) cin >> f[i];
    for (int i = 1; i <= k; i++) cin >> h[i];
    sort(f+1, f+n+1);
    for (int i = 1; i <= n; i++)
    {
        if (f[i] != f[i-1])
        {
            cnt++;
            for (int j = 1; j <= n*k; j++)
            {
                if (f[i] == c[j]) num[cnt]++;
            }
        }
        peo[cnt]++;
    }
    for (int i = 1; i <= cnt; i++)
    {
        memset(dp, 0, sizeof(dp));
        for (int j = 1; j <= peo[i]; j++)
        {
            for (int kk = 1; kk <= num[i]; kk++)
            {
                for (int l = 0; l <= min(k, kk); l++)
                {
                    dp[j][kk] = max(dp[j][kk], dp[j-1][kk-l] + h[l]);
                }
            }
        }
        ans += dp[peo[i]][num[i]];
    }
    cout << ans;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-05 16:21  David24  阅读(90)  评论(0编辑  收藏  举报