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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值