Engineer Assignment(暴力+状压dp)
题意:
n个工程,m个研究员,每个工程需要Ci个领域(X1,X2..Xci)的研究员 ,每个研究员会Di个不同的领域(X1,X2..Xdi),要完成一个工程必须使得分配给这个工程的研究员覆盖了这个工程所需的所有领域。问如何分配研究员可以使能供完成的工程数最多?
n,m<=10;Ci<=3;Di<=2;
思路:
由于n很小,可以枚举出一个工程分配的所有方案(\(2^{11}-1\)),其中人员>3的方案可以舍弃(一定不是最优的)。将状态压缩为1~\(2^{11}-1\)的数,从第一个工程到第n个进行状压dp即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=20;
int c[maxn],d[maxn];
int a[maxn][10],b[maxn][10];
int dp[maxn][1<<11];
int vis[120];
vector<int>V[maxn];
int t,n,m;
void init(){
for(int i=1;i<=n;i++)
V[i].clear();
memset(dp,0,sizeof(dp));
}
int main(){
cin>>t;
for(int kase=1;kase<=t;kase++){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
for(int j=1;j<=c[i];j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=m;i++){
scanf("%d",&d[i]);
for(int j=1;j<=d[i];j++){
scanf("%d",&b[i][j]);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<1<<m;j++){//枚举方案数
memset(vis,0,sizeof(vis));
int cnt=0;
for(int k=0;k<m;k++){//判断集合中有哪些人
if((1<<k)&j){//有id为k+1的人
for(int l=1;l<=d[k+1];l++){
vis[b[k+1][l]]=1;
}
cnt++;
}
}
if(cnt>3)continue;
int flag=1;
for(int k=1;k<=c[i];k++)
if(vis[a[i][k]]==0)
flag=0;
if(flag)V[i].push_back(j);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<1<<m;j++){
dp[i][j]=dp[i-1][j];
for(auto k:V[i]){
if((j|k)==j){//k属于j
dp[i][j]=max(dp[i-1][j-k]+1,dp[i][j]);
}
}
}
}
printf("Case #%d: %d\n",kase,dp[n][(1<<m)-1]);
}
}