题目链接:
code:
//Must so
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define mem(a,x) memset(a,x,sizeof(a))
#define inf (1<<29)
using namespace std;
typedef long long ll;
/*
读题就读了半天=-=
N台电脑,每台有N种服务,同时这N台电脑构成网络(输入就是电脑的关系)
黑客要做的事是,对于每台电脑,选择1种服务去攻击
当攻击电脑x的y服务,和x直接相连的电脑的y服务都会停止
当一种服务在所有N台电脑都是"停止"状态的时候,我们称这种服务"崩溃"
now ,你要做的是,对于每台电脑,选择一种服务去攻击,使得处于"崩溃"状态的服务最多
给的N最大16,显然是要状态压缩的
这样,对于服务i,i在编号0~n-1的n台电脑上被攻击记为1,不被攻击记为0
state[i]记录了满足条件的状态
所谓满足条件是说:例如001100表示在2、3号电脑上攻击可以做到使所有电脑都被攻击
我是转化成01背包的思想=-=
然后把这些满足条件的状态尽可能多的"装"起来
每种状态是一个物品,物品的体积是状态二进制里面的"1",物品的价值的1
装的时候要满足选择装进背包的状态不能有交集
*/
const int N = 1<<16;
vector<int>s[20];
int state[N+5];
int dp[N+5];
int n;
int tot;//记录满足条件的状态总数
bool ok(int x)//判断状态x是否满足条件
{
bool vis[20] = {0};
for (int i = 0;i < n;++i)
{
if ((x>>i)&1)//选择攻击第i台电脑
{
vis[i] = 1;
for (int j = 0;j < s[i].size();++j)
{
vis[s[i][j]] = 1;//和该台被攻击的电脑直接相连的电脑的服务也停止
}
}
}
for (int i = 0;i < n;++i)//判断是否每台电脑都被攻击到了
{
if (!vis[i]) return 0;
}
return 1;
}
/*
剪枝:比如两种状态,000111和000001,
如果第二种状态就ok,当然没必要要第一种状态=-=
*/
vector<int>q;
void init()
{
tot = 0;
q.clear();
int v = (1<<n)-1;
for (int i = 1;i <= v;++i)
{
dp[i] = 0;
bool yes = 0;
for (int j = 0;j < tot;++j)
{
if ((i&q[j]) == q[j])
{
yes = 1;
break;
}
}
if (yes) continue;
if (ok(i))
{
q.push_back(i);
state[tot++] = i;
}
}
}
int main()
{
int kas = 0;
while (~scanf("%d",&n))
{
if (n==0) break;
for (int i = 0;i < n;++i)
{
int m;scanf("%d",&m);
s[i].clear();//初始化=-=
for (int j = 0,x;j < m;++j)
{
scanf("%d",&x);
s[i].push_back(x);
}
}
init();
int V = (1<<n)-1;//背包体积=-=
for (int i = 0;i < tot;++i)
{
for (int j = V;j >= 0;--j)
{
if ((j&state[i]) == 0)//保证不能有交集=-=
{
dp[j|state[i]] = max(dp[j|state[i]] ,dp[j] + 1);
}//二进制的|运算抽象成"加"
}
}
printf("Case %d: %d\n",++kas,dp[V]);
}
return 0;
}