题意:有 n 个工程, m 个人,每个工程需要 ci 个领域的知识,每个人精通 di 个领域。每个人只能去一个工程,问最多可完成多少个工程。
tags:数据范围小,状压
dp[i][j] 代表前 i 个工程在状态 j 的组合下,最多能完成多少个工程。
首先预处理出每个工程可由哪些人组合完成,用 0 ~ ((1<<m)-1)记录。这里有点麻烦。。
然后就是 dp 转移,在考虑到第 i 个工程时:
1】如不取这个工程,则 dp[i][j] = max(dp[i][j] , dp[i-1][j] ) ;
2】如取,则 dp[i][j] = max(dp[i][j] , dp[i-1][j^v]+1 ) , 要求满足 j 包含 v 。
其实类似于背包。。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 200005; int T, n, m, a[15][5], c[15], dp[15][1<<13], di, tmp; vector<int > ve1[15], ve2[110]; void Init() { rep(i,0,14) ve1[i].clear(); rep(i,0,109) ve2[i].clear(); mes(dp, 0); } int main() { scanf("%d", &T); rep(cas,1,T) { scanf("%d%d", &n, &m); Init(); rep(i,1,n) { scanf("%d", &c[i]); rep(j,1,c[i]) scanf("%d", &a[i][j]); } rep(i,0,m-1) { scanf("%d", &di); rep(j,1,di) scanf("%d", &tmp), ve2[tmp].PB(i); } rep(ci,1,n) // Init { for(auto i : ve2[a[ci][1]]) { if(c[ci]==1) { ve1[ci].PB( (1<<i) ); continue; } for(auto j : ve2[a[ci][2]]) { if(c[ci]==2) { ve1[ci].PB( (1<<i)|(1<<j) ); continue; } for(auto l : ve2[a[ci][3]]) ve1[ci].PB( (1<<i)|(1<<j)|(1<<l) ); } } } rep(i,1,n) rep(j,0,(1<<m)-1) { for(auto v : ve1[i]) { if((j|v)==j) dp[i][j] = max(dp[i][j], dp[i-1][j^v]+1); } dp[i][j] = max(dp[i][j], dp[i-1][j]); } int ans = 0; rep(j,0,(1<<m)-1) ans = max(ans, dp[n][j]); printf("Case #%d: %d ", cas, ans); } return 0; }