给出
n
,
m
≤
10
n,m\leq10
n,m≤10分别代表有
n
n
n个工程以及
m
m
m个工程师,然后给出每个工程所需要能力以及每个工程师所具备的能力,求问如果分配工程师使得能够完成的工程最多。
因为数据范围很小,可以直接预处理对于每个工程
i
i
i,所有
2
m
2^m
2m种状态的工程师是否可以完成。那么
f
i
,
S
f_{i,S}
fi,S表示到前
i
i
i个工程,当前工程师的状态是
S
S
S能够完成的最多的工程数。
然后暴力枚举
S
S
S的子集
S
′
S'
S′判断
S
′
S'
S′是否能够完成当前的工程,暴力转移。
#include<bits/stdc++.h>
using namespace std;
const int N=10;
int nd[N][3+1],can[N][2+1];
bool way[N][1<<N];
int f[N][1<<N];
int main() {
int T;
scanf("%d",&T);
int kase=0;
while(T--) {
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) {
scanf("%d",&nd[i][0]);
for(int j=1;j<=nd[i][0];j++)
scanf("%d",&nd[i][j]);
}
for(int i=0;i<m;i++) {
scanf("%d",&can[i][0]);
for(int j=1;j<=can[i][0];j++)
scanf("%d",&can[i][j]);
}
for(int i=0;i<n;i++) {
for(int j=0;j<(1<<m);j++) {
vector<int> all;
way[i][j]=1;
for(int k=0;k<m;k++) {
if(j&(1<<k)) {
for(int w=1;w<=can[k][0];w++) {
all.push_back(can[k][w]);
}
}
}
for(int k=1;k<=nd[i][0];k++) {
bool ok=0;
for(auto &x:all) {
if(x==nd[i][k]) {
ok=1;
break;
}
}
if(!ok) way[i][j]=0;
}
}
}
memset(f,0,sizeof(f));
for(int j=0;j<(1<<m);j++)
f[0][j]=way[0][j];
for(int i=1;i<n;i++) {
for(int j=0;j<(1<<m);j++) {
f[i][j]=max(f[i-1][j],(int)way[i][j]);
for(int k=j;k>0;k=(k-1)&j) {
f[i][j]=max(f[i][j],f[i-1][k]+(way[i][j^k]));
}
}
}
int ans=0;
for(int i=0;i<(1<<m);i++)
ans=max(ans,f[n-1][i]);
printf("Case #%d: %d\n",++kase,ans);
}
return 0;
}