题目大意:n (n<=20) 台计算机连成网络,每台计算机上都同时运行着N种服务。对于每台计算机,可以选择仅一项服务,终止这台计算机和所有与它相邻计算机的该项服务。求:最多可以让多少种服务完全终止运行。
输入:输入一个n表示有n台计算机。之后的n-1行,表示第i个计算机的连接情况,第一个数字表示有m台计算机与它相连,并给出这些计算机的编号。
本题较难,首先是对题意的理解。即将计算机分为几个联通块,使得每个分组都能实现停止一项服务。
由于n较小,则以计算机的分组为压缩状态。
令f(s)表示以s状态下能停止服务最多,cover[s]表示s联通块。
f(s)=max( f(s) , f(s0) + 1) 其中s0是s的子集,cover[s]为所有计算机。
#include <iostream>
#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n ,node[25] ,f[1<<17] ,cover[1<<17] ,end ,a ,code ;
int main()
{
while(~scanf("%d",&n)&&n)
{
memset(cover,0,sizeof(cover));
for(int i=0;i<n;++i)
{
node[i]=1<<i;
scanf("%d",&a);
for(int i=0;i<a;++i)
{
scanf("%d",&a);
node[i] |= 1<<a;
}
}
end=(1<<n)-1;
//预处理cover数组 cover[s]表示在s的情况下 s中的计算机所组成的联通块
for(int s=0;s<=end;++s)
for(int i=0;i<n;++i)
if(s & (1<<i))
cover[s] |= node[i];
for(int s=0;s<=end;++s)
{
f[s]=0;
for(int s0=s;s0;s0=(s0-1)&s)//求s的子集
if(cover[s0]==end)
f[s]=max(f[s],f[s0]+1);
}
printf("Case %d: %d\n",++code,f[end]);
}
return 0;
}