题意:
给定最多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; }