【UVA11825】Hackers' Crackdown

探讨黑客如何通过精心选择攻击策略最大化破坏效果,在有限时间内破坏尽可能多的服务。利用子集DP解决网络中节点间相互影响的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

  Miracle Corporations has a number of system services running in a distributed computer system which is a prime target for hackers. The system is basically a set of N computer nodes with each of them running a set of N services. Note that, the set of services running on every node is same everywhere in the network. A hacker can destroy a service by running a specialized exploit for that service in all the nodes.
  One day, a smart hacker collects necessary exploits for all these N services and launches an attack on the system. He finds a security hole that gives him just enough time to run a single exploit in each computer. These exploits have the characteristic that, its successfully infects the computer where it was originally run and all the neighbor computers of that node.
  Given a network description, find the maximum number of services that the hacker can damage.

题面

  有n个节点,每一个节点都有n种标记,现在可以对每一个节点分别进行一次操作,每一次操作可以选择一种标记,使这个节点及与其相邻的节点覆盖这种标记(一个节点可以被覆盖多种标记),问经过n操作后,最多有多少种标记覆盖了所有节点
  n16,节点编号从0开始

解法

子集DP
  第一次做子集DP的题……
  这个题目的意思就是说:全集为U|U|=n,有n个集合,为{P1P2Pn},现在要对这n个集合进行分组,要求P1P2Pk=UP为该组内的集合,求最多能分多少组
  设stai为一个二进制数,表示第i个节点相邻的点及自身,例如i=10(2)stai=10110(2),表示与1号节点相邻的有4,2两个节点。由此可知,stai表示对i进行操作能够标记的点的集合
  设pi表示选择i表示的集合能够标记的点集,i为一个二进制数,例如i=10010(2)表示选择{0,4}这个集合能够标记的点集

  设fi表示选择i集合能够标记的全集个数,即答案,i也是一个二进制数。状态已经设出来了,下面就是如何转移:
  对于一种状态ii表示的就是一个集合,考虑如何枚举出i的所有子集,可以参见刘汝佳的紫书

复杂度

O(2n*平均非空子集个数),当有n个物品时,总非空子集数为:ni=1Cin

代码

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define Lint long long int
using namespace std;
const int N=20;
const int L=(1<<17);
int f[L+5],p[L+5];
int sta[N];
int n,A;
int main()
{
    int len,x,C=0;
    while( scanf("%d",&n) && n )
    {
        memset( sta,0x0,sizeof sta );
        memset( f,0x0,sizeof f ),memset( p,0x0,sizeof p );
        for(int i=0;i<=n-1;i++)
        {
            scanf("%d",&len);
            for(int j=1;j<=len;j++)
            {
                scanf("%d",&x);
                sta[i]|=(1<<x);
            }
            sta[i]|=(1<<i);
        }
        A=(1<<n)-1;
        for(int i=0;i<=A;i++)
        {
            p[i]=0;
            for(int j=0;j<=n-1;j++)
                if( i&(1<<j) )
                    p[i]|=sta[j];
        }
        for(int i=0;i<=A;i++)
        {
            if( p[i]!=A )   f[i]=0;
            else   f[i]=1;
            for(int j=i; j ;j=(j-1)&i)
                f[i]=max( f[i],f[j]+f[i^j] );
        }
        printf("Case %d: %d\n",++C,f[A]);
    }
    return 0;
}
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值