UVA11825. Hackers' Crackdown(枚举子集DP)

题目链接:https://vjudge.net/contest/370423#problem/B
题意有点难理解,也就是给出N个结点,每个结点关联它自身和一些其他的点,每当所有结点都被破坏时,相当于破坏了一个服务。询问最多可以破坏多少组服务。
解题思路:
①p[i]表示第i个结点关联的点,用二进制表示

for(int i=0;i<n;i++){
			int cnt;
			scanf("%d",&cnt);
			p[i]=1<<i;
			for(int j=0;j<cnt;j++){
				int temp;
				scanf("%d",&temp);
				p[i]|=1<<temp;
			}
		}

②然后二进制枚举所有情况,令len=(1<<(n+1))-1种情况(这里也可以从0位开始,一共有1<<n-1种情况),然后利用c[i]保存一个状态可以破坏的结点情况

for(int i=1;i<=len;i++){
			for(int j=0;j<n;j++){
				if(i&(1<<j))
					c[i]|=p[j];
			}
		}

③最后用dp来遍历二进制所有情况,以及情况对应的子集
第一个i循环是遍历所有的二进制情况
第二个j循环,是遍历i情况的所有子集,来保持动态,更新状态
条件是这个子集满足能够破坏所有结点

	for(int i=1;i<=len;i++){
			for(int j=i;j;j=(j-1)&i){
				if(c[j]==len)  //如果j情况可以破坏所有的结点,则算作一次
					dp[i]=max(dp[i],dp[i^j]+1);
			}
		}

④最终结果一定是破坏所有情况下即1111111…时能够破坏的服务器数

#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<vector>
using namespace std;
const int maxn=1<<16;
int n; 
int p[maxn];
int c[maxn];
int dp[maxn];
int main()
{
	int t=0;
	while(scanf("%d",&n)!=EOF){
		t++;
		if(n==0)  break;
		memset(dp,0,sizeof(dp));
		memset(p,0,sizeof(p));
		memset(c,0,sizeof(c));
		for(int i=0;i<n;i++){
			int cnt;
			scanf("%d",&cnt);
			p[i]=1<<i;
			for(int j=0;j<cnt;j++){
				int temp;
				scanf("%d",&temp);
				p[i]|=1<<temp;
			}
		}
		int len=(1<<n)-1;
		for(int i=1;i<=len;i++){
			for(int j=0;j<n;j++){
				if(i&(1<<j))
					c[i]|=p[j];
			}
		}
		for(int i=1;i<=len;i++){
			for(int j=i;j;j=(j-1)&i){
				if(c[j]==len)
					dp[i]=max(dp[i],dp[i^j]+1);
			}
		}
		printf("Case %d: %d\n",t,dp[len]);
	}
	return 0;
}
		
原创文章 65 获赞 3 访问量 2083

猜你喜欢

转载自blog.csdn.net/littlegoldgold/article/details/105912818