题意:给你若干个集合,让你把这些集合做划分使的每个划分的并集是一个全集,问你最多能进行多少个划分。
思路:
首先了解下子集枚举的方法
for (int i = s; i; i = (i - 1) &s)
i - 1使得末尾最右边的1右边的0变成1,自己变成0,然后经过与运算把不存在的1删掉,原来是0的位无论如何也不会变成1,但是原来是1的位就形成了不断-1的模式。
有了这个前置技能,这题就变简单了。首先用一个p数组状压每个子集能得到的状态。再用s数组表示每个划分方式能得到的状态。
然后枚举全部状态s的子集s0,f[s]=max(f[s],f[s^s0]+1)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int p[105];int b[1<<16];int f[1<<16];
int main()
{
int n;int cas=1;
while(scanf("%d",&n)&&n)
{
memset(p,0,sizeof(p));
for(int i=0;i<n;i++)
{
int m;cin>>m;
p[i]=(1<<i);
for(int j=0;j<m;j++)
{
int x;cin>>x;
p[i]|=(1<<x);
}
}
for(int s=0;s<(1<<n);s++)
{
b[s]=0;
for(int i=0;i<n;i++)
{
if(s&(1<<i))
{
b[s]|=p[i];
}
}
}
f[0]=0;
int all=(1<<n)-1;
for(int s=1;s<(1<<n);s++)
{
f[s]=0;
for(int s0=s;s0;s0=(s0-1)&s)
{
if(b[s0]==all) f[s]=max(f[s],f[s^s0]+1);
}
}
printf("Case %d: %d\n", cas++, f[all]);
}
return 0;
}