uva -11825 Hackers' Crackdown(dp专题C)

题目链接http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18913

题目大意:黑客入侵了一个包含n台电脑的网络,每台电脑上都运行了相同的n种服务,每台电脑直接相连一些其他的电脑。黑客可以对每台电脑安装一种病毒k(一台电脑只能安装一种),病毒会导致与该台电脑直接相连的(包括本身)电脑无法提供第k种服务,当网络中没有电脑可以提供第k种服务时(每台电脑上都感染病毒k),则称该种服务瘫痪。求最多可以使几种服务瘫痪.

样例解读:(给每台电脑标号0~(n-1))

样例二:

4

1 1

1 0

1 3

1 2

如样例:0和1连,1和0连,2和3连,3和2连。

所以在第0台和第2台种植k0病毒,第1台和第3台种植k1病毒。则所有的电脑感染k0,k1病毒。最多两种服务完全瘫痪。

问题转化:

因为0和2组成的电脑以及它们直接相连的电脑包含所有电脑,所以在这两台上放置病毒k0就可以让k0服务瘫痪。我们把n台电脑分组,并且每一组电脑及和它们直接相邻的电脑都包含所有的电脑,这样的组数就是可以导致多少种服务瘫痪。所以我们找尽可能多的组数。

思路:

1)用s[i]保存i及和i直接相连的电脑(eg.s[0] = 3 (11) 表示第0台电脑和第1台电脑相邻)

然后枚举所有电脑分组的可能,枚举的方法很简单,遍历1~(1<<n)

2)用cover数组存储每一种分组包含的电脑的个数,cover[i] = j(eg. cover[3] = 3,表示第0台和第1台电脑组成的组包含第0

和第1台电脑,cover[5] = 15,第0和第2台电脑组成的组包含第0,1,2,3台电脑)

3)最后用dp[i]记录,i的二进制表示的电脑能组成多少组包含所有电脑的组。则状态转移方程为

dp[i] = max(dp[i],dp[s0^i]+1)(其中s0&i=k且cover[s0] = (1<<n)-1,即s0的二进制表示的电脑的组包含所有的电脑)

注:枚举每一个s0的方法,s0 =(s0-1)& 1,直至s0为0.

说的比较啰嗦,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 16;

int s[maxn];
int cover[1<<maxn];
int dp[1<<maxn];
int main(){
    int n;
    int ca = 1;
    while(cin>>n&&n){
        for(int i = 0;i<n;i++){
            int m;
            cin>>m;
            s[i] = 1<<i;
            for(int j = 0;j<m;j++){
                int a;
                cin>>a;
                s[i] |=(1<<a);
            }
        }
        memset(cover,0,sizeof(cover));
        for(int i = 0;i<(1<<n);i++){
            for(int j = 0;j<n;j++){
                if((1<<j)&i){
                    cover[i]|=s[j];
                }
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i = 0;i<(1<<n);i++){
            for(int s0 = i;s0;s0 = (s0-1)&i){
                if(cover[s0] == (1<<n)-1)
                    dp[i] = max(dp[i],dp[s0^i]+1);
            }
        }
        cout<<"Case "<<ca++<<": ";
        cout<<dp[(1<<n)-1]<<endl;
    }
    return 0;
}

dp专题B: http://blog.csdn.net/sinat_30062549/article/details/51291613


你可能感兴趣的:(dp,uva,状态压缩,状压)