1. 程式人生 > >UVA 11825 - Hackers' Crackdown 狀態壓縮 dp 枚舉子集

UVA 11825 - Hackers' Crackdown 狀態壓縮 dp 枚舉子集

... 節點 ble hack 一個 tex wid cas 大小

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


UVA 11825 - Hackers&#39; Crackdown 狀態壓縮 dp 枚舉子集