题意:有N个贼,M个地窖(N,M<=50)
每个贼有个背包,容量为A1,A2.....AN
每个地窖里有X(X <= 25)件东西,每件东西的价值为B1,B2.。。。BX(<=10000000)
贼都很贪,只会去偷能把自己背包装满的地窖,求最多能偷到多少东西
这个问题要分成两个阶段考虑……
一是谁能偷哪个地窖,问题转化成了01背包问题……由于数据太大,咱们只能搜……
然后,就是匹配问题,KM可以,另外,按照賊的背包容量从大到小顺序匈牙利也行……
于是,对每个地窖,一个Naive的实现:
void dfs(int lv,int num,int deep) { if (num > X[N - 1]) return; if (lv == deep) { visit[num] = mark; return; } dfs(lv + 1, num + A[lv], deep); dfs(lv + 1, num, deep); }
复杂度 2^25,产生了这个地窖所有能拼出来的可能数
然后还得枚举,复杂度 50 * 50 << 25 …… 似乎进入了TLE的节奏……
看了解题报告才明白,Meet-in-middle……也就是DFS一半,哈希起来……然后另一边进去查找……
经过优化,复杂度变成 50 * Max(1 << 15,50 << 10)……
P.S:如果去阿三赛区会不会有拿金进Final的希望……昨天单挑都做了6道,按榜看来已然前12……
第7道差这么个Meet in middle优化……7道前三……
完整代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int N,M; int adj[55][55]; int A[55]; int X[55]; int visit[10000001]; int deep; int mark = 1; int match[55]; bool vv[55]; void dfs(int lv,int num,int deep) { if (num > X[N - 1]) return; if (lv == deep) { visit[num] = mark; return; } dfs(lv + 1, num + A[lv], deep); dfs(lv + 1, num, deep); } void dfs_2(int lv,int num,int deep,int fa) { if (num > X[N - 1]) return; if (lv == deep) { for (int j = 0; j < N; j++) { if (X[j] - num >= 0 && visit[X[j] - num] == mark) { adj[j][fa] = X[j]; } } return; } dfs_2(lv + 1, num + A[lv], deep, fa); dfs_2(lv + 1, num, deep, fa); } bool dfs(int k) { if (vv[k]) return false; vv[k] = true; for (int i = 0; i < M; i++) { if (adj[k][i]) { int tt = match[i]; match[i] = k; if (tt == -1 || dfs(tt)) return true; match[i]=tt; } } return false; } int main() { int nn; scanf("%d",&nn); while (nn--) { scanf("%d%d",&N,&M); memset(adj,0,sizeof(adj)); memset(A,0,sizeof(A)); for (int i = 0; i < N; i++) scanf("%d",X + i); sort(X,X + N); for (int i = 0; i < M; i++) { scanf("%d",&deep); for (int j = 0; j < deep; j++) scanf("%d",A + j); mark ++; if (deep < 15) { dfs(0,0,deep); for (int j = 0; j < N; j++) { if (visit[X[j]] == mark) { adj[j][i] = X[j]; } } } else { dfs(0,0,15); dfs_2(15,0,deep,i); } for (int j = 0; j < N; j++) { if (visit[X[j]] == mark) { adj[j][i] = true; } } } memset(match,0xff,sizeof(match)); int ans = 0; for (int i = N - 1; i >= 0; i--) { memset(vv,0,sizeof(vv)); if (dfs(i)) ans += X[i]; } printf("%d\n",ans); } return 0; }