Hackers' Crackdown(UVA-11825)(狀壓DP+枚舉子集)

 

題意:假設你是一個黑客, 侵入了一個有着n臺計算機(編號爲0,1,…,n-1) 的網絡。 一共有n種服務,每臺計算機都運行着所有服務。對於每臺計算機,你都可以選擇一項服務, 終止這臺計算機和所有與它相鄰計算機的該項服務(如果其中一些服務已經停止, 則這些服務繼續處於停止狀態)。 你的目標是讓儘量多的服務器完全癱瘓(即: 沒有任何計算機運行該項服務)。

思路:這道題的話,要注意的地方是如果一個電腦被入侵了,那麼它的相鄰的電腦的相鄰的電腦也會是被入侵了。
p[i]表示的與i直接相鄰的一個狀態,沒太大直接意義,主要是爲了推出c的狀態,c[i]表示的是i狀態被選擇時,n個電腦被覆蓋的狀態。dp[i]表示i狀態被選擇,已經被入侵的最大服務數。用到枚舉子集,假設集合i爲j的一個子集,則(i-1)&j爲j的下一個子集,直至i爲0。其實就是一個狀態能夠達到全集,然後和另外一個狀態合併成一個新的狀態,然後就是dp取最大值。

AC代碼:

#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <math.h>
typedef long long ll;
const int maxx=20;
const int inf=0x3f3f3f3f;
using namespace std;
int p[maxx];
int c[1<<maxx];
int dp[1<<maxx];
int main()
{
    int n;
    int k=1;
    while(~scanf("%d",&n),n)
    {
        int a,b;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a);
            p[i]=1<<i;//左移,構造一個只有第i+1位是1的二進制數,爲了將0表示在第一位上,故這樣操作
            while(a--)
            {
                scanf("%d",&b);
                p[i]|=(1<<b);//與i直接相連的電腦的表示
            }
        }
        c[0]=0;
        for(int i=1; i<=(1<<n); i++)
        {
            c[i]=0;//i表示枚舉所有電腦組成的情況
            for(int j=0; j<n; j++)
            {
                if(i&(1<<j))
                    c[i]|=p[j];//若此位上有電腦則將其所有相鄰的電腦及本身加入此集合
            }
        }
        int ans=(1<<n)-1;//只有第n+1位沒有電腦,即所有n臺都被感染
        for(int i=1; i<(1<<n); i++)
        {
            dp[i]=0;
            for(int j=i; j; j=(j-1)&i)//子集枚舉,減少電腦數與原集合取交集
            {
                if(c[j]==ans)//每臺電腦都運行着所有的服務,題意要求每臺電腦可以選擇關一項服務,如果c[j]==all表示如果關掉(j在二進制下的表示)的電腦上的某服務,必有一項服務在所有電腦上都被關掉
                    dp[i]=max(dp[i],dp[i^j]+1);
            }
        }
        printf("Case %d: %d\n",k++,dp[ans]);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章