博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UVA11825 黑客的攻击 Hackers' Crackdown 状压DP,二进制,子集枚举
阅读量:5984 次
发布时间:2019-06-20

本文共 1823 字,大约阅读时间需要 6 分钟。

【题目描述】

假如你是一个黑客,侵入了一个有着\(n\)台计算机(编号为\(1.2.3....n\))的网络。一共有\(n\)种服务,每台计算机都运行着所有服务。对于每台计算机,你都可以选择一项服务,终止这台计算机和所有与它相邻计算机的该项服务(如果其中一些服务已经停止,那他们继续保持停止状态)。你的目标是让尽量多的服务完全瘫痪(即:没有任何计算及运行着该服务)

【输入格式】

输入包含多组数据,每组数据的第一行为整数\(n(1<=n<=16)\):以下\(n\)行每行描述一台计算机相邻的计算机,其中第一个数\(m\)为相邻计算机个数,接下来的\(m\)个整数为这些计算机的编号。输入结束标志\(n=0\)

【输出格式】

对于每组数据,输出完全瘫痪的服务的数量。


本题实际上可以转化为:给你\(n\)个集合\(p_{1 -> n}\),你要把它们分成尽可能多的组,每个组内所有集合的并等于全集。

因为\(n\)比较小,所以我们可以把每个集合\(P\)(每个点自身\(+\)它相邻的点)二进制状压。考虑选取一些集合时,把选取的集合也二进制状压(表示为\(S\)),存一下该选取状态下可以覆盖的状况即可(\(cover_s\))。

这样我们可以得到方程:

\[f(S) = max (f(S - S_0)|S_0∈S, cover_{S_0} = S_{All})\]

技巧:二进制下的子集枚举:

for (int S0 = S; S0 != 0; S0 = (S0 - 1) & S)

这样为什么能实现子集枚举呢?请读者自行思考(笑

复杂度:\(O(\sum_{k=1->N}C(n, k) * 2 ^ n) = O(3 ^ n)\)。为什么等于后面我不会二项式定理所以不大会。

关注点:本题中的子集枚举思想。

#include 
using namespace std;const int N = 20;int Case, n, m, to, s[N], f[N], cho[1 << N];int main () {// freopen ("data.in", "r", stdin); while (cin >> n && n) { for (int i = 0; i < n; ++i) { cin >> m; s[i] = 1 << i; for (int j = 0; j < m; ++j) { cin >> to; s[i] |= 1 << to; } // cout << "s[" << i << "] = " << s[i] << endl; } const int All = (1 << n) - 1; for (int i = 0; i < 1 << n; ++i) { cho[i] = 0; for (int k = 0; k < n; ++k) { if ((i >> k) & 1) { cho[i] |= s[k]; } } } f[0] = 0; for (int S = 1; S < (1 << n); ++S) { f[S] = 0; for (int S0 = S; S0; S0 = (S0 - 1) & S) { //枚举S的子集 if (cho[S0] == All) { f[S] = max (f[S], f[S ^ S0] + 1); } } } cout << "Case " << ++Case << ": " << f[All] << endl; }}

转载于:https://www.cnblogs.com/maomao9173/p/10688004.html

你可能感兴趣的文章