UVa 11825 - Hackers' Crackdown DP, 枚举子集substa = (substa - 1)&sta 难度: 2
题目
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2925
题意
n个节点,每个节点都有完全相同的n项服务。
每次可以选择一个节点,破坏该节点和相邻节点的某项服务。
问最多能完全破坏多少服务?
思路
如刘书,
直接枚举状态的子集
注意元素个数为k的集合有C^k_n个子集,那么枚举的时间复杂度为sum{c^k_n * 2^k} = 3^n,当n=16时,3^n=4e7,可以承受。
注意枚举子集可以通过substa = (substa - 1)&sta来做,子集的补集则为substa ^ sta。
感想
1. 一开始觉得枚举时间是2^2n,觉得不行,还是缺乏细致的计算
代码
#include <algorithm> #include <cassert> #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <queue> #include <set> #include <string> #include <tuple> #define LOCAL_DEBUG using namespace std; typedef pair<int, int> MyPair; const int MAXN = 16; const int MAXSTA = 1 << 16; int edges[MAXN][MAXN]; int edgeCnt[MAXN]; int vis[MAXN]; int vis2[MAXSTA]; int dp[MAXSTA]; int main() { #ifdef LOCAL_DEBUG freopen("C:\\Users\\Iris\\source\\repos\\ACM\\ACM\\input.txt", "r", stdin); //freopen("C:\\Users\\Iris\\source\\repos\\ACM\\ACM\\output.txt", "w", stdout); #endif // LOCAL_DEBUG int n; for (int ti = 1; scanf("%d", &n) == 1 && n; ti++) { for (int i = 0; i < n; i++) { scanf("%d", edgeCnt + i); for (int j = 0; j < edgeCnt[i]; j++) { scanf("%d", edges[i] + j); } } int maxsta = (1 << n) - 1; for (int sta = 0; sta <= maxsta; sta++) { memset(vis, 0, sizeof vis); for (int i = 0; i < n; i++) { if (sta & (1 << i)) { vis[i] = true; for (int j = 0; j < edgeCnt[i]; j++) { vis[edges[i][j]] = true; } } } bool fl = true; for (int i = 0; i < n; i++) { if (!vis[i]) { fl = false; } } if (fl) { dp[sta] = 1; } else { dp[sta] = 0; } } for (int sta = 0; sta <= maxsta; sta++) { for (int subSta = sta; subSta != 0; subSta = (subSta - 1) & sta) { dp[sta] = max(dp[sta], dp[sta ^ subSta] + dp[subSta]); } } printf("Case %d: %d\n", ti, dp[maxsta]); } return 0; }