UVA11825 黑客的攻击 Hackers' Crackdown 状压DP,二进制,子集枚举

题目链接Click Here

【题目描述】

假如你是一个黑客,侵入了一个有着\(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 <bits/stdc++.h>
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值