问题描述
小A有 个朋友,他现在正在过 天的假期,他希望在这 天中每天都找一个朋友玩,一旦选择了一个朋友,将不能再选择其他人。 天假期结束后,如果其中任何一个朋友被选择次数严格超过 次,那么所有其他朋友都会被冒犯。 当然,小A不想冒犯任何人,请你帮助他选择队友。
输入
第一行包含测试用例的数量 。 测试用例的说明如下:
每个测试用例的第一行包含两个整数 和 ,分别代表好友数和游戏天数。
以下 行中的第 行包含整数 ,后跟 个不同的整数 ,以空格分隔,表示第 天有空的朋友索引
保证所有测试用例的 和 的总和不超过100000。
保证所有测试用例的所有天的所有 的总和不超过200000。
输出
输出为每个测试用例打印答案。
如果无法达到目标,请打印 "NO"。
否则,在第一行中打印 "YES",在第二行中打印 个空格分隔的整数。 每个 表示在第 天选择的队友。任何 不得出现超过 次。如果可能的答案不止一个,请打印其中的任何一个。
分析
首先需要注意是,问题所需要的并不是最优解,而仅仅是满足条件的一个解,因此再考虑问题的时候千万不能钻牛角尖,非要向最优解的方向考虑。其次,本题的关键在于对条件 的利用,注意到一旦有一个朋友的出现次数超过了 ,那么其他所有出现的次数都不会超过 ,即最多只有一个朋友是超过 的,记为B。那么我们只需要通过替换,将B的出现次数下降到 ,那么此时是一定符合条件的(仔细体会这句话)。所以只需遍历一次天数,若当天选择的是B,且还有其他朋友可供选择,则从中随机选一个朋友与B替换。一旦B的出现次数下降到 则直接跳出循环,当前保留的朋友数组有效。当且仅当可供替代的朋友不足时,B的次数无法下降到 ,此时输出为“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";
}
}
}