UVA 11825 Hackers' Crackdown dp+位运算状态压缩(子集枚举)

题意:

      有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)
*/ 


你可能感兴趣的:(UVA 11825 Hackers' Crackdown dp+位运算状态压缩(子集枚举))