[UVA 11825] Hackers' Crackdown

图片加载可能有点慢,请跳过题面先看题解,谢谢
1196604-20171012150422371-656313214.png
1196604-20171012150427355-2137018233.png
1196604-20171012150431449-26317639.png

这道题目把模型简化后其实很简单,简化后的模型为:
有许多个小集合 \(P0,P2...Pn-1\) (即每个节点和与它相邻节点的并为 \(P\) ),现在要求出一种分组方式,使得每一组的元素的并为全集并且组数最多
$
$
我们注意到,\(n\) 很小,完全可以将 \(P\) 压成一个二进制数,那么这就是一个比较简单的子集 \(dp\) 了。
\(vis[i]\) 为,在 \(i\) 状态下的覆盖情况(\(i\) 是一个二进制数,表示 \(P\) 的选取状态);
\(f[i]\) 为, 在 \(i\) 状态下能破坏的最大服务数(\(i\) 同上),则答案为 \(f[(1<<n)-1]\)
$
$
那么转移就很好写了:$f[i]=max(f[i],f[i $ ^ j]+1)\(,其中,\)\(j\)\(i\) 的子集且 \(vis[j]=(1<<n)-1\)
另附枚举 \(i\) 的子集 \(j\) 的方式:for(j=i;j;j=(j-1)&i)

//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int t,n,p[54],vis[1<<20],f[1<<20];

il void init(){
   for(int i=0;i<n;i++){
      p[i]=1<<i; RG int m=gi();
      for(int j=1;j<=m;j++){
         RG int x=gi(); p[i]|=1<<x;
      }
   }
}

il void work(){
   int ans=0,Max=(1<<n)-1;
   for(int i=0;i<=Max;i++){ vis[i]=0;
      for(int j=0;j<n;j++)
         if(i&(1<<j)) vis[i]|=p[j];
   }
   f[0]=0;
   for(int i=1;i<=Max;i++){ f[i]=0;
      for(int j=i;j;j=(j-1)&i)
         if(vis[j]==Max) f[i]=max(f[i],f[i xor j]+1);
   }
   printf("Case %d: %d\n",++t,f[Max]);
}

int main(){ while(scanf("%d",&n)&&n){ init(); work(); } return 0; }

转载于:https://www.cnblogs.com/Hero-of-someone/p/7656314.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值