题意:
给定最多30个集合让划分成L个部分(每部分由若干完整集合组成,L<=min(5,n)),每个集合内最多10个元素,元素都是小于300的正整数,问给定一组集合能不能划分成
L部分且每个部分的集合最少有一个公共元素。
分析:
这样的题目用确定一个基本点的方法,进行枚举,首先1集合必定在L个集合中的一个,那么可以直接限定它出现在第一个集合,那么枚举10个元素到底哪一个值是公共的,然后标记所有有这个元素的集合,再找到第一没有这个元素的集合继续如此,如果递归<=L层,就覆盖了所有集合,那么一定存在可行解,否则当前方案不合法。
这样的枚举是高效的最多5层,每层10个分支,转移为o(n),这样的话复杂度最坏为n*1e5。
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
#define rep1(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) for(int i=0;i<(int)n;i++)
const int N = 33;
int n,L;
int deg[N],ah[N][330],a[N][N],vis[N];
bool flag;
void dfs(int p,int hav ,int cnt){
if(flag) return ;
if(hav == L) return ;
int ok = 0;
rep1(j,1,deg[p]){
int v = a[p][j];
vector<int> cc;
rep1(k,j,n) if(!vis[k] && ah[k][v]) cc.push_back(k),vis[k]=1;//,cout<<k<<"** ";
int nexp = -1;
rep1(k,j,n) if(!vis[k]) {nexp = k; break;}
if(nexp == -1) {flag = true; return ;}
dfs(nexp,hav+1,cnt+cc.size());
rep(k,cc.size()) vis[cc[k]] = 0;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&L);
memset(ah,0,sizeof(ah));
rep1(i,1,n) {
scanf("%d",°[i]);
rep1(j,1,deg[i]) scanf("%d",&a[i][j]),ah[i][a[i][j]]=1;
}
flag = false;
memset(vis,0,sizeof(vis));
dfs(1 , 0 , 0);
printf("%s\n",flag ? "YES":"NO");
}
return 0;
}