UVA - 11825 (状压+子集枚举)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37632935/article/details/86536648

题意:给你若干个集合,让你把这些集合做划分使的每个划分的并集是一个全集,问你最多能进行多少个划分。

思路:

首先了解下子集枚举的方法

   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;
}

猜你喜欢

转载自blog.csdn.net/qq_37632935/article/details/86536648