UVA 11825 Hackers' Crackdown

时间:2023-03-09 00:26:10
UVA 11825 Hackers' Crackdown

题目大意就是有一个图,破坏一个点同时可以破坏掉相邻点。每个点可以破坏一次,问可以完整破坏几次,点数=16。

看到16就想到状压什么的。

尝试设状态:用f[i]表示选的情况是i(一个二进制串),至少可以破坏几次。

那么就有这样的转移方式:

1.选走所有人用来破坏一次。

2.选举i的一个子集sub,f[i]=f[sub]+f[i-sub]。

然后发现这题就做完了。

具体做法:把每个人的破坏情况预处理一下,然后枚举全集i。

先全部选,看是否能破坏一层。

然后枚举子集,取max。

枚举子集方式:for(int sub=i;sub;sub=(sub-1)&i)。

一道颇有纪念意义的状压子集DP,复杂度O(3^n)。

#include    <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
#include <queue>
#include <complex>
#include <stack>
#define LL long long int
#define dob double
#define FILE "11825"
using namespace std; const int N = ;
int bin[],n,can[N],ban[],t; inline int gi(){
int x=,res=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')res*=-;ch=getchar();}
while(ch<=''&&ch>='')x=x*+ch-,ch=getchar();
return x*res;
} int main()
{
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
bin[]=;for(int i=;i<=;++i)bin[i]=bin[i-]*;
while(n=gi()){
memset(can,,sizeof(can));
for(int i=;i<n;++i)ban[i]=bin[i];
for(int i=;i<n;++i)
for(int j=gi();j>=;--j)
ban[i]|=bin[gi()];
for(int opt=;opt<bin[n];++opt){
for(int i=;i<n;++i)if(opt&bin[i])can[opt]|=ban[i];can[opt]/=(bin[n]-);
for(int sub=(opt-)&opt;sub;sub=(sub-)&opt)
can[opt]=max(can[opt],can[sub]+can[opt^sub]);
}
printf("Case %d: %d\n",t,can[bin[n]-]);
}
fclose(stdin);fclose(stdout);
return ;
}

Hackers' Crackdown