UVA11825 Hackers' Crackdown

Source

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2925

Solution

首先要对题目做一个等价转换,题目等价于将集合 {{p1},{p2}...{pn}} { { p 1 } , { p 2 } . . . { p n } } 划分成若干个子集,使尽量多的子集中集合的并集都等于全集 U U U表示 n n 个节点的并集,集合{pi}是每个点和它相邻节点构成的集合。
这是一道状压 DP D P ,用 f[i] f [ i ] 表示状态为 i i 时的最大获利,其中状态i的每个二进制位记录的是这个集合选或没选,
状态转移方程为 f[S]=max{f[SS0]+1,S0SS0 f [ S ] = m a x { f [ S − S 0 ] + 1 , S 0 是 S 的 子 集 且 S 0 包 含 了 全 部 n } 个 节 点 }
这个方程的理解就是,对于集合 S S ,我枚举它的子集,看看这个子集是不是能够完全包含n个节点,如果可以,就更新答案。
对于集合的 check c h e c k ,看他是不是包含了全部 n n 个点,可以提前与处理,这样复杂度就是枚举S和子集的复杂度
S S 一共有2n种,但是对于每种集合, S0 S 0 的数量不同, S0 S 0 的数量应该是 2|S| 2 | S |
用组合数算一下, ni=1Cin2i=ni=02i1ni1 ∑ i = 1 n C n i 2 i = ∑ i = 0 n 2 i 1 n − i − 1 = 3n1 3 n − 1
所以时间复杂度是 Θ(3n) Θ ( 3 n )
316=43,046,721 3 16 = 43 , 046 , 721 数量级高达 107 10 7 ,但是因为我们在做位运算,因此很快
取模如果这么大时间复杂度的话,肯定已经超时了

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define clear(x) memset(x,0,sizeof(x))
#define maxn 20
using namespace std;
int n, p[maxn], f[1<<16], ok[1<<16];
bool check(int S)
{
    int t, i;
    for(i=t=0;i<n;i++)if(S&(1<<i))t|=p[i];
    return t==(1<<n)-1;
}
bool init()
{
    int i, m, x;
    scanf("%d",&n);
    if(n==0)return 0;
    clear(p);
    clear(ok);
    for(i=0;i<n;i++)for(scanf("%d",&m),p[i]=1<<i;m--;)scanf("%d",&x),p[i]|=1<<x;
    for(i=0;i<1<<n;i++)if(check(i))ok[i]=1;
    return 1;
}
void dp()
{
    int S, s;
    clear(f);
    for(S=0;S<1<<n;S++)for(s=S;s;s=(s-1)&S)if(ok[s])f[S]=max(f[S],f[S^s]+1);
}
int main()
{
    int c;
    for(c=1;init();c++)
    {
        printf("Case %d: ",c);
        dp();
        printf("%d\n",f[(1<<n)-1]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值