假设你是一个黑客,侵入了一个有着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;
}