【题意】给定n个问题和K个人,给定每个人知道的问题列表,求能否找到一个非空问题集合,满足每个人知道的集合中问题数量都不超过集合总题数的一半。n<=10^5,k<=4。
【算法】数学结论
【题解】当k<=4时,结论:若存在合法方案,则一定存在选择2个问题的合法方案。
证明:对于选择偶数个问题的合法方案(奇数不优),假设方案中每个人都知道一半问题(最坏),试图从中得到选择2个问题的合法方案。
先得到三条易证的结论:
Ⅰ两个人知道区间互补,则找不到问题满足这两个人都不知道。
Ⅱ否则,至少存在一个问题满足这两个人都不知道。
Ⅲ多个人知道区间一致,则至少存在一个问题满足这多个人都不知道。
①若不存在Ⅰ,则把4个人分成2组,每组内由Ⅱ可得1个问题。如果2个问题一致说明这两人对应那两人区间分别一致,由Ⅲ可知交换搭档即可。
②若存在Ⅰ,则把互补分到不同组。若一个人和另外三个人都互补,另外三个人由Ⅲ得到1个问题,一个人再得到1个问题。
得证。
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #define ll long long using namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } /*------------------------------------------------------------*/ const int inf=0x3f3f3f3f; int n,kind,a[100010]; bool c[10000]; int main(){ n=read();kind=read(); for(int i=1;i<=n;i++){ for(int j=1;j<=kind;j++)a[i]=(a[i]<<1)+read(); c[a[i]]=1; } if(c[0]){printf("YES");return 0;} for(int i=1;i<(1<<kind);i++)if(c[i]){ int x=i^((1<<kind)-1); for(int j=1;j<(1<<kind);j++)if(c[j]&&(x&j)==j){printf("YES");return 0;} } printf("NO"); return 0; }