本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@TOC
Технокубок 2021 - Финал-C. Basic Diplomacy
传送门 Time Limit: 2 seconds Memory Limit: 512 megabytes
Problem Description
Aleksey has friends. He is also on a vacation right now, so he has days to play this new viral cooperative game! But since it's cooperative, Aleksey will need one teammate in each of these days.
On each of these days some friends will be available for playing, and all others will not. On each day Aleksey must choose one of his available friends to offer him playing the game (and they, of course, always agree). However, if any of them happens to be chosen strictly more than times, then all other friends are offended. Of course, Aleksey doesn't want to offend anyone.
Help him to choose teammates so that nobody is chosen strictly more than times.
Input
Each test contains multiple test cases. The first line contains the number of test cases (). Description of the test cases follows.
The first line of each test case contains two integers and () standing for the number of friends and the number of days to play, respectively.
The -th of the following lines contains an integer (), followed by distinct integers , ..., (), separated by spaces — indices of available friends on the day .
It is guaranteed that the sums of and over all test cases do not exceed . It's guaranteed that the sum of all over all days of all test cases doesn't exceed .
Output
Print an answer for each test case. If there is no way to achieve the goal, print "NO".
Otherwise, in the first line print "YES", and in the second line print space separated integers , ..., . Each must denote the chosen friend on day (and therefore must be one of ).
No value must occur more than times. If there is more than one possible answer, print any of them.
Sample Input
2
4 6
1 1
2 1 2
3 1 2 3
4 1 2 3 4
2 2 3
1 3
2 2
1 1
1 1
Sample Onput
YES
1 2 1 1 2 3
NO
题目大意
要与个朋友度过天。 一天只与一个朋友玩,一个朋友累计玩耍次数不得超过(向上取整) 给你每天可以玩耍的朋友列表,问你能不能找到一种玩耍方法符合上述条件。
解题思路
贪心。关键是如何贪。
首先给你了天的朋友列表,每天的可玩人数不同。可玩人数少的最难满足,顾先满足人数少的那些天。 这就需要第一次排序:按可玩朋友数量,对不同的天数进行排序。
比如样例1:
4 6
1 1
2 1 2
3 1 2 3
4 1 2 3 4
2 2 3
1 3
共有个朋友,共需要玩天。 第一天有1个朋友,编号为1 第二天有2个朋友,编号为1、2 第6天有1个朋友,编号为3
初始状态:
1 | |||
---|---|---|---|
1 | 2 | ||
1 | 2 | 3 | |
1 | 2 | 3 | 4 |
2 | 3 | ||
3 | |||
按每天的朋友数量进行排序: | |||
1 | |||
-- | -- | -- | -- |
3 | |||
1 | 2 | ||
2 | 3 | ||
1 | 2 | 3 | |
1 | 2 | 3 | 4 |
注意,题目可没说给你的朋友序号是按从小到大给的,所以他可能给你个2 1 3
之类的,但是不影响。
然后开始贪心。
假如某天有朋友
1 3 4
,而1
已经出现了9
次了,3
只出现了3
次,4
出现了5
次。 那么首选肯定用3
,因为3
出现过的次数少。如果
1
已经出现了9
次了,3
只出现了3
次,而4
也出现了3
,那么4
和3
选谁呢?继续观察,发现这天之后(已排序)3
还能再出现2
次,而4
不会再出现了。 那么肯定首选4
,因为不选白不选,选了后面也不会再出现了。
因此,我们得到了对于具体的某一天的优先方法:可用次数多的优先(then 后面出现次数少的优先,这个可有可无,无所谓了)。
技巧
- 数据量较大,无法开 [1e5] [1e5]的数组。故用vector<vector<int> >来存。
- 第一次排序会打乱原有的天数,故在每天的vector的末尾,插入一个数,来记录这个vector是那一天的。排序之后再取出。
AC代码
/*
* @Author: LetMeFly
* @Date: 2021-03-21 22:34:14
* @LastEditors: LetMeFly
* @LastEditTime: 2021-03-22 21:07:59
*/
#include<bits/stdc++.h>
using namespace std;
#define mem(a) memset(a,0,sizeof(a))
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define fi(i,l,r) for(int i=l;i<r;i++)
#define cd(a) scanf("%d",&a)
typedef long long ll;
bool cmp1(vector<int>&a,vector<int>&b)//根据每天的朋友列表的人数进行排序
{
if(a.size()!=b.size())//人数少的优先
return a.size()<b.size();
for(int i=0;i<a.size();i++)//人数相同的情况下,编号小的优先
{
if(a[i]!=b[i])
return a[i]<b[i];
}
return 1;
}
int HouMianChuXian[200010];//HouMianChuXian[i]表明i在后面的天数中还会出现多少次
int KeYongCiShu[200010];//KeYongCiShu[i]表示i还可以再出现几次
int ans[200010];//答案。因为第一次排序打乱了天数,故答案不是按顺序得到的,需要先记录一下
bool cmp2(int a,int b)//对于具体的某一天,根据贪心原则进行排序
{
if(KeYongCiShu[a]!=KeYongCiShu[b])//可以出现的次数不相同
return KeYongCiShu[a]>KeYongCiShu[b];//还可以出现的次数多的优先
if(HouMianChuXian[a]!=HouMianChuXian[b])
return HouMianChuXian[a]<HouMianChuXian[b];//其实cmp2可以简化,简化版本的cmp2见下方
if(a!=b)//上述条件全部相同,按编号进行排序(非必要)
return a<b;
return 1;
}
int main()
{
int N;
cin>>N;
while(N--)//N组测试样例
{
int n,m;
scanf("%d%d",&n,&m);
int ZuiDuoYueJiCi=m/2+m%2;//最多出现的次数
for(int i=1;i<=n;i++)
{
HouMianChuXian[i]=0;//初始化,后面出现的次数为0
KeYongCiShu[i]=ZuiDuoYueJiCi;//初始化,还可以出现的次数为最多出现的次数
}
vector<vector<int> >a;//2维vector
for(int i=0;i<m;i++)//m天的每一天
{
vector<int>thisVector;//今天的vector
int thisFriend;//这次出现的朋友
int k;
scanf("%d",&k);//今天有k个朋友
for(int j=0;j<k;j++)
{
scanf("%d",&thisFriend);//这个朋友是thisFriend
HouMianChuXian[thisFriend]++;//后面出现thisFriend的次数+1
thisVector.push_back(thisFriend);//插入vector
}
thisVector.push_back(i+1);//tips: 尾部插入今天的天数,以便排序后使用
a.push_back(thisVector);//把今天的vector插入到总的大vector中
}
sort(a.begin(),a.end(),cmp1);//第一次排序,根据当天出现的人数进行排序
bool cannot=0;//flag,不行吗? 默认false
for(int i=0;i<m;i++)//共有m天
{
vector<int>thisDay=a[i];//要处理的这一天的vector
vector<int>::iterator it=thisDay.end();
it--;
int thisDayNum=*it;//获得排序前这天是第几天
thisDay.erase(it);//去掉最后一个代表天数的数字
sort(thisDay.begin(),thisDay.end(),cmp2);//对具体的这一天,根据优先级进行排序
for(int j=0;j<thisDay.size();j++)
{
HouMianChuXian[thisDay[j]]--;//这天过后,这天出现的每个朋友再次出现的次数-1
}
int thisDaysFriend=thisDay[0];//选择最为优先的朋友
KeYongCiShu[thisDaysFriend]--;//这个朋友还可以出现的次数-1
if(KeYongCiShu[thisDaysFriend]<0)//如果这个朋友还可以出现的次数已经为负了
{
cannot=1;//不行
}
ans[thisDayNum]=thisDaysFriend;//记录下答案:当天的选择的朋友
}
if(cannot)//如果不行
{
puts("NO");
}
else//否则可以
{
puts("YES");
for(int i=1;i<=m;i++)
{
printf("%d ",ans[i]);//输出答案:每天选择的朋友
}
puts("");
}
}
return 0;
}
cmp2简单版本:
bool cmp2(int a,int b)//对于具体的某一天,根据贪心原则进行排序
{
if(KeYongCiShu[a]!=KeYongCiShu[b])//可以出现的次数不相同
return KeYongCiShu[a]>KeYongCiShu[b];//还可以出现的次数多的优先
return 1;
}
同步发文于我的CSDN,原创不易,转载请附上原文链接哦~
Tisfy:blog.csdn.net/Tisfy/artic…