题意: n(n<=10)个工程 m(<=10)个工程师。每个工程需要一些技术(技术个数<=3) 一个工程师精通一些技术(技术个数<=2) 。一个工程师只能去一个工程干活,一个工程师可以担任一个工程里的多个技术。问最多多少个工程可以开工。技术种类数<=100。
思路: dp[i][S]: 前 i 个工程,S表示1-m个工程师选与不选的状态。
dp[i][S]=max(dp[i-1][S],dp[i][S^vec[i][k]]+1).
vec[i][k]是预处理出来哪种状态可以使得工程 i 完工。假如这种状态需要的人数>3,那么这种不放入vector 其实是为了优化,很显然啊,总共需要3个技术 怎么可能需要3个以上的人呢,这也对,但是没必要用这种状态转移dp。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define m(a,b) memset(a,b,sizeof a)
using namespace std;
const int N=1e5+5;
int a[11][4],b[11][3],vis[101],dp[11][(1<<10)];
vector<int>vec[11];
int main(){
int T,cas=0;scanf("%d",&T);
while(T--){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i][0]);
for(int j=1;j<=a[i][0];++j) scanf("%d",&a[i][j]);
}
for(int i=1;i<=m;++i){
scanf("%d",&b[i][0]);
for(int j=1;j<=b[i][0];++j) scanf("%d",&b[i][j]);
}
int t=(1<<m);
for(int i=1;i<=n;++i){//将每个工程合法的需要的工程师的方案预处理
for(int j=1;j<t;++j){
m(vis,0);int num=0;
for(int k=1;k<=m;++k){
if(j&(1<<(k-1))){
++num;if(num>3) break;//人数不超过3个 相当于减少不优的转移状态
for(int o=1;o<=b[k][0];++o) vis[b[k][o]]=1;
}
}
int flag=0;
for(int o=1;o<=a[i][0];++o)
if(!vis[a[i][o]]) {flag=1;break;}
if(num<=3&&!flag) vec[i].push_back(j);
}
}
int ans=0;
for(int i=1;i<=n;++i){//dp[i][j]=max(dp[i-1][j],dp[i-1][j^vec[i][k]]+1);
for(int j=0;j<t;++j){
dp[i][j]=dp[i-1][j];
for(int k=0;k<vec[i].size();++k)
if((j|vec[i][k])==j) dp[i][j]=max(dp[i][j],dp[i-1][j^vec[i][k]]+1);//j|vec[i][k]==j
}
}
for(int j=0;j<t;++j) ans=max(ans,dp[n][j]);
printf("Case #%d: %d\n",++cas,ans);
if(T){
for(int i=1;i<=n;++i) vec[i].clear();
for(int j=0;j<t;++j) dp[0][j]=0;
}
}
}