/** 题目:hdu6006 Engineer Assignment 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6006 题意:已知n个工程,每个需要某些领域的专家。有m个工程师,每个人擅长一些领域。 m<=10 一个工程师只能参加一个工程。一个工程可以多个工程师参加。 如果参加某个工程的工程师他们擅长的领域覆盖了该工程需要的领域。那么该工程可以执行。 问最多可以执行多少个工程。 思路: 定义dp[i][s]表示前i个工程状态为s可以执行的最大工程数。s表示前i个工人选走了s状态的工程师。 dp[i][s] = max(dp[i][s],dp[i-1][s']+1); s'为s的子集,且f[i][s-s'] = 1; 表示i这个工程,分配s-s'的工程师,可以完成。 */ #include<iostream> #include<cstdio> #include<algorithm> #include<map> #include<vector> #include<cstring> using namespace std; typedef long long LL; const int N = 2e5+100; const int inf = 0x3f3f3f3f; vector<int> pro[12], eng[12]; map<int,int> mp; int f[11][1<<10]; int mv[12]; int rm[1<<20]; int n, m; int dp[2][1<<10]; void init() { mv[0] = 0; for(int i = 1; i <= m; i++){ int s = 0; for(int j = 0; j < eng[i].size(); j++){ s |= 1<<(mp[eng[i][j]]-1); } mv[i] = s; } int len = (1<<m); rm[0] = 0; for(int i = 1; i < len; i++){ rm[i] = 0; for(int j = 1; j <= m; j++){ if(i&(1<<(j-1))){ rm[i] |= mv[j]; } } } memset(f, 0, sizeof f); for(int i = 1; i <= n; i++){ int s1 = 0; for(int j = 0; j < pro[i].size(); j++){ if(mp[pro[i][j]]==0){ s1 = 0; break; } s1 |= 1<<(mp[pro[i][j]]-1); } if(s1==0) continue; for(int j = 1; j < len; j++){ if((rm[j]&s1)==s1){ f[i][j] = 1; } } } } int main() { int cas = 1, T; cin>>T; while(T--) { scanf("%d%d",&n,&m); int num, x; for(int i = 1; i <= n; i++){ scanf("%d",&num); pro[i].clear(); for(int j = 1; j <= num; j++){ scanf("%d",&x); pro[i].push_back(x); } } mp.clear(); int cnt = 1; for(int i = 1; i <= m; i++){ scanf("%d",&num); eng[i].clear(); for(int j = 1; j <= num; j++){ scanf("%d",&x); eng[i].push_back(x); if(mp[x]==0){ mp[x] = cnt++; } } } int len = (1<<m); init(); int now = 0; for(int i= 0; i < len; i++) dp[0][i] = 0; for(int i = 1; i <= n; i++){ now ^= 1; for(int s = 0; s < len; s++){ dp[now][s] = dp[now^1][s];///根据定义,所以传递。 if(f[i][s]==0) continue; for(int s0 = s; s0; s0 = (s0-1)&s){ if(f[i][s0]) dp[now][s] = max(dp[now][s],dp[now^1][s-s0]+1); } } } printf("Case #%d: %d ",cas++,dp[now][len-1]); } return 0; }