cf_719_2c题解

77 阅读2分钟

问题描述

小A有 nn 个朋友,他现在正在过 mm 天的假期,他希望在这 mm 天中每天都找一个朋友玩,一旦选择了一个朋友,将不能再选择其他人。mm 天假期结束后,如果其中任何一个朋友被选择次数严格超过 m/2⌈m/2⌉ 次,那么所有其他朋友都会被冒犯。 当然,小A不想冒犯任何人,请你帮助他选择队友。

输入

第一行包含测试用例的数量 t1t10000t(1≤t≤10000)。 测试用例的说明如下:

每个测试用例的第一行包含两个整数 nnm1nm100000m(1≤n,m≤100000) ,分别代表好友数和游戏天数。

以下 mm 行中的第 ii 行包含整数 ki1kinki(1≤ki≤n) ,后跟 kiki 个不同的整数 fi1...fiki(1fijn)fi1,...,fiki(1≤fij≤n) ,以空格分隔,表示第 ii 天有空的朋友索引

保证所有测试用例的 nnmm 的总和不超过100000。

保证所有测试用例的所有天的所有 kiki 的总和不超过200000。

输出

输出为每个测试用例打印答案。

如果无法达到目标,请打印 "NO"。

否则,在第一行中打印 "YES",在第二行中打印 mm 个空格分隔的整数c1...cmc1,...,cm。 每个 cici 表示在第 ii 天选择的队友。任何 cici 不得出现超过 m/2⌈m/2⌉ 次。如果可能的答案不止一个,请打印其中的任何一个。

分析

首先需要注意是,问题所需要的并不是最优解,而仅仅是满足条件的一个解,因此再考虑问题的时候千万不能钻牛角尖,非要向最优解的方向考虑。其次,本题的关键在于对条件 m/2⌈m/2⌉ 的利用,注意到一旦有一个朋友的出现次数超过了 m/2⌈m/2⌉ ,那么其他所有出现的次数都不会超过 m/2⌈m/2⌉ ,即最多只有一个朋友是超过 m/2⌈m/2⌉ 的,记为B。那么我们只需要通过替换,将B的出现次数下降到 m/2⌈m/2⌉ ,那么此时是一定符合条件的(仔细体会这句话)。所以只需遍历一次天数,若当天选择的是B,且还有其他朋友可供选择,则从中随机选一个朋友与B替换。一旦B的出现次数下降到 m/2⌈m/2⌉ 则直接跳出循环,当前保留的朋友数组有效。当且仅当可供替代的朋友不足时,B的次数无法下降到 m/2⌈m/2⌉ ,此时输出为“NO”。

代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t; cin >> t;
    while(t--) {
        int n, m; cin >> n >> m;
        vector<vector<int>> frids(m);
        vector<int> cnt(n, 0);
        vector<int> ans(m);
        int my = 1;
        for(int i = 0; i < m; i++) {
            int x; cin >> x;
            int y; cin >> y;
            cnt[y-1]++;
            ans[i] = y;
            if(cnt[y-1] > cnt[my-1]) my = y;

            while(--x) {
                cin >> y;
                frids[i].emplace_back(y);
            }
        }

        int lim = (m+1) / 2;
        // cnt[my-1] > lim 时需要进行替换
        for(int i = 0; i < m; i++) {
            if(cnt[my-1] <= lim) break;
            if(frids[i].empty() || ans[i] != my) continue;

            ans[i] = frids[i][0];
            cnt[my-1]--;
        }

        if(cnt[my-1] <= lim) {
            cout << "YES\n" << ans[0];
            for(int i = 1; i < m; i++) {
                cout << " " << ans[i];
            }
            cout << '\n';
        }
        else {
            cout << "NO\n";
        }
    }
}