黑客的攻击(UVA 11825)

来源:互联网 发布:烟台龙口淘宝招聘网 编辑:程序博客网 时间:2024/05/20 07:34

题目

假设你是一个黑客,侵入了一个有着n台计算机(编号为0,1…,n-1)的网络。一共有n种服务,每台计算机都运行着所有服务。对于每台计算机,你都可以选择一项服务,终止这台计算机和所有与它相邻计算机的该项服务(如果其中一些服务已经停止,则这些服务继续处于停止状态)。你的目标是让尽量多的服务完全瘫痪(即:没有任何计算机运行该项服务)。

输入格式

输入包含多组数据,每组数据的第一行为整数n(1≤n≤16);以下n行每行描述一台计算机的相邻计算机,其中第一个数m为相邻计算机个数,接下来的m个整数为这些计算机的编号。输入结束标志位n=0。

输出格式

对于每组数据,输出完全瘫痪的服务器的最大数量。

算法分析

将这道题转换下,其实就是有集合P0…Pn-1(这些集合分别代表编号为0…n-1的计算机和与他们相邻的计算机)现在要把这些集合分成尽量多的组,使得每组中所有集合的并集为全集n。注意到这道题的n非常的小,所以可以考虑用二进制(1代表相邻,0代表不相邻)。
那么这道题的状态转移方程也不难想到:
用f(S)表示集合S最多能分成多少组,状态转移方程则为
f(S)=max{f(S-S0)|S0是S的子集,S0中所有集合Pi的并集为全集}+1
为了方便起见,使用cover数组表示集合S中所有Pi的并集(二进制表示)


这里写代码片
import java.util.Scanner;

public class Main {
static int maxV = 1 << 16;
static int[] cover = new int[maxV];
static int[] p = new int[maxV];
static int[] f = new int[maxV];

public static void main(String[] args) {    Scanner sc = new Scanner(System.in);    int n = 0;    int Case = 0;    while ((n = sc.nextInt()) != 0) {        for (int i = 0; i < n; i++) {            int m = sc.nextInt();            p[i] = 1 << i;// 要和自己本身相邻            while (m != 0) {                int x = sc.nextInt();                p[i] |= 1 << x;                m--;            }        }        for (int S = 0; S < (1 << n); S++) {            cover[S] = 0;            for (int i = 0; i < n; i++) {                if ((S & (1 << i)) != 0) {                    cover[S] |= p[i];                }            }        }        f[0] = 0;        int All = (1 << n) - 1;        for (int S = 1; S < (1 << n); S++) {            f[S] = 0;            for (int S0 = S; S0 != 0; S0 = (S0 - 1) & S) {                if (cover[S0] == All) {                    f[S] = Integer.max(f[S], f[S ^ S0] + 1);                }            }        }        System.out.println("Case " + (++Case) + ":" + f[All]);    }    sc.close();}

}
这里要注意一点,两集合的非(减法运算)就是两集合做异或运算。

时间复杂度分析

很显然,这个算法的时间复杂度等于所有cover子集的总和,也就是sum{2^(S0)|S0为全集的子集}(一个集合A的子集个数为2^(|A|))。
由上述公式可知,元素个数相同的集合,其子集个数也必然相同。那么我们就可以按照元素个数进行合并求和。元素个数为k的集合有C(n,k)个(从{1,2,…n}中选出k个元素),其中每个集合有2^k个子集,因此本题的时间复杂度为O(sum{C(n,k)*2^k})=O((2+1)^n)(由二项式定理可得)=O(3^n)

0 0