zoukankan      html  css  js  c++  java
  • UVA11825 Hackers' Crackdown

    状压DP

    首先感谢lrj的透彻讲解.

    我们要使一项服务瘫痪,就必须选择一些计算机,使它们与他们所相连的计算机是所有的计算机,即:我们将每一个计算机本身及其相连的计算机看成一个集合(P_i),我们要分成尽量多的集合,使每一个集合里(Pi∪...∪Pj)为全集。

    我们又发现n的值比较小,因此我们可以考虑一下状压,(P_i)的每一位表示(i)号计算机的联通情况,(C_i)(这是数组,不是组合数)表示我们选择该二进制位下所选择的计算机的并集,(f(C_i))表示在二进制下(C_i)可以分成的最大答案。

    那么我们就要枚举全集子集的子集,对于子集,我们循环就行了,对于子集的子集,我们这样写就行了(虽然我也不知道为什么)学长说记住就行了

    for(int S=0;S<=maxn;++S)//全集的子集 
    for(int s0=S;s0;s0=(s0-1)&S)//全集的子集的子集
    

    关于这样的时间复杂度,我们知道一个大小为(n) 的集合的子集的个数是(2^n=sum_{k=0}^{n}C_{n}^{k})(这里是组合数),那么一个集合的子集的子集的个数就是(sum_{k=0}^{n} C_{n}^{k}2^k),根据二项式定理((a+b)^k=sum_{k=0}^{n}C_n^k a^kb^{n-k}),我们逆用后就能得到原式为((2+1)^n=3^n),所以时间复杂度为(O(3^n))O(能过)

    剩下的就是一个简单的DP啦

    另外用位运算也可以优化一下常数。

    最后献上我丑陋的代码。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    int n,m,x,maxn,my_case;
    int p[21],c[1<<18],f[1<<18];
    int main()
    {
    	while(scanf("%d",&n)&&n)
    	{
    		maxn=(1<<n)-1;
    		memset(c,0,sizeof(c));memset(f,0,sizeof(f));
    		for(int i=0;i<n;++i)
    		{
    			p[i]=(1<<i);scanf("%d",&m);
    			while(m--){scanf("%d",&x);p[i]|=(1<<x);}
    		}
    		
    		for(int S=0;S<=maxn;++S)
    			for(int i=0;i<n;++i) if(S&(1<<i))c[S]|=p[i];
    		
    		for(int S=0;S<=maxn;++S)//全集的子集 
    			for(int s0=S;s0;s0=(s0-1)&S)//全集的子集的子集 
    			if(c[s0]==maxn)f[S]=max(f[S],f[S-s0]+1);
    		
    		printf("Case %d: %d
    ",++my_case,f[maxn]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Nginx下配置SSL安全协议
    Java嵌套类
    python命名空间与作用域
    Effective Java 4:类和接口
    VIM快捷键(转载)
    Eclipse快捷键
    Effective Java 3:对于所有对象都通用的方法
    Effective Java 1:创建和销毁对象-续
    Effective Java 1:创建和销毁对象
    python文件操作(转载)
  • 原文地址:https://www.cnblogs.com/wljss/p/11508791.html
Copyright © 2011-2022 走看看