题目链接http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18913
题目大意:黑客入侵了一个包含n台电脑的网络,每台电脑上都运行了相同的n种服务,每台电脑直接相连一些其他的电脑。黑客可以对每台电脑安装一种病毒k(一台电脑只能安装一种),病毒会导致与该台电脑直接相连的(包括本身)电脑无法提供第k种服务,当网络中没有电脑可以提供第k种服务时(每台电脑上都感染病毒k),则称该种服务瘫痪。求最多可以使几种服务瘫痪.
样例解读:(给每台电脑标号0~(n-1))
样例二:
4
1 1
1 0
1 3
1 2
如样例:0和1连,1和0连,2和3连,3和2连。
所以在第0台和第2台种植k0病毒,第1台和第3台种植k1病毒。则所有的电脑感染k0,k1病毒。最多两种服务完全瘫痪。
问题转化:
因为0和2组成的电脑以及它们直接相连的电脑包含所有电脑,所以在这两台上放置病毒k0就可以让k0服务瘫痪。我们把n台电脑分组,并且每一组电脑及和它们直接相邻的电脑都包含所有的电脑,这样的组数就是可以导致多少种服务瘫痪。所以我们找尽可能多的组数。
思路:
1)用s[i]保存i及和i直接相连的电脑(eg.s[0] = 3 (11) 表示第0台电脑和第1台电脑相邻)
然后枚举所有电脑分组的可能,枚举的方法很简单,遍历1~(1<<n)
2)用cover数组存储每一种分组包含的电脑的个数,cover[i] = j(eg. cover[3] = 3,表示第0台和第1台电脑组成的组包含第0
和第1台电脑,cover[5] = 15,第0和第2台电脑组成的组包含第0,1,2,3台电脑)。
3)最后用dp[i]记录,i的二进制表示的电脑能组成多少组包含所有电脑的组。则状态转移方程为
dp[i] = max(dp[i],dp[s0^i]+1)(其中s0&i=k且cover[s0] = (1<<n)-1,即s0的二进制表示的电脑的组包含所有的电脑)
注:枚举每一个s0的方法,s0 =(s0-1)& 1,直至s0为0.
说的比较啰嗦,代码如下:
#include<bits/stdc++.h> using namespace std; const int maxn = 16; int s[maxn]; int cover[1<<maxn]; int dp[1<<maxn]; int main(){ int n; int ca = 1; while(cin>>n&&n){ for(int i = 0;i<n;i++){ int m; cin>>m; s[i] = 1<<i; for(int j = 0;j<m;j++){ int a; cin>>a; s[i] |=(1<<a); } } memset(cover,0,sizeof(cover)); for(int i = 0;i<(1<<n);i++){ for(int j = 0;j<n;j++){ if((1<<j)&i){ cover[i]|=s[j]; } } } memset(dp,0,sizeof(dp)); for(int i = 0;i<(1<<n);i++){ for(int s0 = i;s0;s0 = (s0-1)&i){ if(cover[s0] == (1<<n)-1) dp[i] = max(dp[i],dp[s0^i]+1); } } cout<<"Case "<<ca++<<": "; cout<<dp[(1<<n)-1]<<endl; } return 0; }