NKOJ3720 黑客攻击 [状态压缩][背包DP]

NKOJ3720 黑客攻击 [状态压缩][背包DP]

问题描述

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

输入格式

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

输出格式

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

样例输入

3
2 1 2
2 0 2
2 0 1
4
1 1
1 0
1 3
1 2
0

样例输出

Case 1: 3
Case 2: 2

解法

范围这么小,当然是考虑状压或者搜索了。这里又密切和每台电脑的状态相关联,基本上就是状压了。

首先要把每台电脑的关系给压出来。比如样例Case 2中 (11) ( 11 ) 表示0和1号电脑相连。几台电脑相连意味着其中的任意一台电脑被停止,那么与它相连的所有电脑的任意一项服务都会被停止。

题目要求完全瘫痪,因此我们要想办法凑出(11…1)(n个1)的状态。任选几台电脑,只要与它们相连的电脑构成的集合(并集)覆盖了所有服务器,那么攻击这些电脑就可以使得服务器完全瘫痪。

设定状态 f[s] f [ s ] 表示攻击集合 s s 中的电脑最多能够使得多少服务器完全瘫痪。这时只要找出 s s 尽可能多的子集(每个子集的攻击范围都能覆盖所有服务器)即可。这表示为方程就是:f[s]=max{f[s^ss]}+1 (ss是s的子集 且 ss的攻击范围覆盖了所有服务器)

代码

#include
#include
#include
using namespace std;
int A[20],B[66000],f[66000];
//A[i]表示第i台电脑与哪些电脑相连
//B[i]用来表示攻击s集合中的能够使得哪些服务器瘫痪
int main(){
    int n,cnt=0;scanf("%d",&n);
    while(n){
        cnt++;
        for(int i=0;iint t,tt=0;scanf("%d",&t);
            tt|=(1<for(int j=1;j<=t;j++){
                int ttt;scanf("%d",&ttt);
                tt|=(1<1]=tt;
        }
        int nn=(1<1;
        for(int s=1;s<=nn;s++)
            for(int i=0;iif(s&(1<1];
        for(int i=1;i<=nn;i++)
            for(int j=i;j;j=(j-1)&i)
                if(B[j]==nn)f[i]=max(f[i],f[i^j]+1);
        printf("Case %d: %d\n",cnt,f[nn]);
        memset(f,0,sizeof(f));
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        scanf("%d",&n);
    }
    return 0;
}

你可能感兴趣的:(题解,动态规划与递推,状态压缩)