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;
}
		
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Buyi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值