题意:
有N台电脑(编号为0,1,2......n-1),每台电脑有N(N<=16)个服务,一个黑客可以在每台电脑上种植让其中一种服务崩溃的病毒,并且这个病毒可以传播到跟他邻接的电脑,问最多这个黑客可以让多少个服务崩溃?(每台电脑上都是N个不同服务,每台电脑上跑的都是一样的服务,让一个服务崩溃是让所有的电脑上的这个服务崩溃)
Input
There will be multiple test cases in the input file. A test case begins with an integer N (1 ≤ N ≤ 16),the number of nodes in the network. The nodes are denoted by 0 to N − 1. Each of the followingN lines describes the neighbors of a node. Line i (0 ≤ i < N) represents the description of node i.The description for node i starts with an integer m (Number of neighbors for node i), followed by mintegers in the range of 0 to N − 1, each denoting a neighboring node of node i.
The end of input will be denoted by a case with N = 0. This case should not be processed.
Output
For each test case, print a line in the format, ‘Case X: Y ’, where X is the case number & Y is themaximum possible number of services that can be damaged.
Sample Input
3
2 1 2
2 0 2
2 0 1
4
1 1
1 0
1 3
1 2
0
Sample Output
Case 1: 3
Case 2: 2
分析:
本题的数学模型:
把n个集合p1,p2,p3...pn分成尽量多组,并且使得每一组集合的并集等于全集S。
f[s]表示集合至多能分成几组,
注意到n<=16,于是每台电脑与其他电脑的关系,也就是前面说的p1,p2...pn,可以用一个二进制数来表示:
相邻的为1,不相邻的为0,全集全为1;
方程:
f(s)= max{f(s-S0) | S0是s的子集,并且crash[S0]==全集S}+1;
其中,crach[S]表示集合S关系到的所有的p[i]的并集(用位运算的'|‘来表达)。
时间的复杂度等于{1,2,3......n}的所有子集的子集的个数。总之用很牛逼的方法可以知道,它是O(3^n)。
代码如下:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int maxn=16; int n,f[(1<<maxn)+5],crash[(1<<maxn)+5],p[maxn]; void input(){ int i,s,m,x; scanf("%d",&n); for(i=0;i<n;i++){ scanf("%d",&m); p[i]= 1<<i; //最初只有自己 while(m--){ scanf("%d",&x); p[i]|=(1<<x); } } memset(crash,0,sizeof(crash)); for(s=0;s<(1<<n);s++){ //预处理crash[s]; crash[s]=0; for(i=0;i<n;i++) if(s&(1<<i))crash[s]|=p[i]; //并集操作 } } void solve(int kase){ int all= (1<<n)-1,s,s0; for(s=1;s<(1<<n);s++){ f[s]=0; //多组数据。不要忘了初值 for(s0=s;s0;s0=(s0-1)&s) //枚举s的子集s0; if(crash[s0]==all) f[s]=max(f[s],f[s-s0]+1); //也可写成f[s^s0]+1; } printf("Case %d: %d\n",kase,f[all]); } int main(){ for(int i=1;;i++){ input(); if(n==0)return 0; solve(i); } } /* 枚举子集的模型: 对于集合s: for(s0=s;s0;s0=(s0-1)&s) */