UVA 11825 - Hackers' Crackdown 状态压缩 dp 枚举子集

UVA 11825 - Hackers' Crackdown 状态压缩 dp 枚举子集

ACM

题目地址:11825 - Hackers' Crackdown

题意: 
有一个由编号0~n-1的n台计算机组成的网络,一共有n种服务,每台计算机上都运行着全部服务,对于每台计算机,你可以选择停止一项服务,这个行为会导致与这台计算机和与他相连的其他计算机上的这项服务都停止(原来已经停止的继续保持停止状态)。求最多能使多少个服务瘫痪(即没有任何一台计算机在运行这项服务)。

分析: 
题目说白了,就是: 
把n个集合p[i],0<=i<n分成尽量多组,使得每组中各个集合的并集为全集。 
利用状态压缩,记录每个节点运行的服务。由于数据大小就16所以直接可以用int范围数字表示一个集合。 
然后预处理下cover,处理16个节点组成的各个集合会带来的挺服务效果。 
然后dp,如果cover[S0] == all (all全为1) 那么是S^S0 的部分也有可能终止服务 ,dp[S] = max(dp[S], dp[S^S0]+1)

参考了凌乱的心巨巨的题解,嘛,是为了了解枚举子集做的题目。

枚举子集的模板:

  
  
  1. // 对于集合S
  2. for (int S0 = S; S0; S0 = S&(S0 - 1)) // 枚举S0为子集
  3. ...

原理:S&(S0 - 1) 实际上是把S中的0全部忽略,并不断减1的结果。

代码

/*
*  Author:      illuz <iilluzen[at]gmail.com>
*  File:        11825.cpp
*  Create Date: 2014-06-27 20:43:48
*  Descripton:  sub set/ dp/ numeric 
*/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 16;

int n, m, t, mask[N], cover[1<<N], dp[1<<N], tot;

int main() {
	int cas = 0;
	while (~scanf("%d", &n) && n) {
		// input
		for (int i = 0; i < n; i++) {
			scanf("%d", &m);
			mask[i] = (1 << i);
			while (m--) {
				scanf("%d", &t);
				mask[i] |= (1 << t);
			}
		}

		// get the union set of cover
		for (int S = 0; S < (1 << n); S++) {
			cover[S] = 0;
			for (int i = 0; i < n; i++) {
				if (S & (1 << i)) {
					cover[S] |= mask[i];
				}
			}
		}

		// dp
		dp[0] = 0;
		tot = (1 << n) - 1;
		for (int S = 1; S < (1 << n); S++) {
			dp[S] = 0;
			for (int S0 = S; S0; S0 = (S0 - 1)&S) {
				if (cover[S0] == tot) {
					dp[S] = max(dp[S], dp[S^S0] + 1);
				}
			}
		}

		printf("Case %d: %d\n", ++cas, dp[tot]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值