传送门

Description

  你要杀n个怪,每杀掉一个怪那个怪会掉落一种武器,这种武器可以杀死特定的怪。游戏初始你有一把武器,能杀死一些怪物。每次只能杀一只,求有多少种杀怪方法。

Input

  多组数据,第一行是数组组数T,对于每组数据,有:

  • 第一行是怪物个数n
  • 第二行以0/1串的形式描述初始武器能杀死的怪物
  • 下面n行,第i行以0/1串的形式描述干掉第i只怪以后掉落武器能杀死的怪物

Output

  对于每组数据,输出:

  • 杀怪的顺序数,形式为Case X: Y

Sample Input


Sample Output

Case :
Case :
Case :

Hint

n≤16

Solution

看看数据范围,大概是个状压DP。

记录kldi为集合i在二进制意义下代表的怪物编号被干掉后掉落武器能干掉的怪物的集合。

由于有一把初始武器,我们可以认为干掉任意集合的怪物都能干掉初始武器能干掉怪物的集合。

设fi为干掉集合i在二进制意义下代表的怪物的顺序个数

枚举该集合的每个元素,考虑该元素的补集被干掉后能不能干掉他,若能,则转移。

转移用到加法原理,方程为fi+=f| j是i中的元素且kld[j^i]&j。

Code

#include<cstdio>
#include<cstring>
#define rg register
#define ci const int
#define cl const long long int typedef long long int ll; inline void qr(int &x) {
char ch=getchar(),lst=NULL;
while(ch>''||ch<'') lst=ch,ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
if (lst=='-') x=-x;
} char buf[];
inline void write(int x,const char aft,const bool pt) {
if(x<) {putchar('-');x=-x;}
int top=;
do {
buf[++top]=x%+'';
x/=;
} while(x);
while(top) putchar(buf[top--]);
if(pt) putchar(aft);
} template <typename T>
inline T mmax(const T &a,const T &b) {if(a>b) return a;return b;}
template <typename T>
inline T mmin(const T &a,const T &b) {if(a<b) return a;return b;}
template <typename T>
inline T mabs(const T &a) {if(a<) return -a;return a;} template <typename T>
inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;} const int maxn = ;
const int maxt = ; int t,cnt,n;
ll frog[maxt];
int gorf[maxn],kld[maxt]; void clear(); int main() {
qr(t);
while(t--) {
clear();
qr(n);
for(rg int j=;j<n;++j) {
char ch=getchar();while((ch!='') && (ch!='')) ch=getchar();
if(ch=='') kld[]|=(<<j);
}
for(rg int i=;i<n;++i) {
for(rg int j=;j<n;++j) {
char ch=getchar();while((ch!='') && (ch!='')) ch=getchar();
if(ch=='') kld[<<i]|=(<<j);
}
}
rg int upceil = (<<n)-;
for(rg int i=;i<=upceil;++i) {
for(rg int j=;j<n;++j) if((<<j)&i) {
kld[i]|=kld[<<j];
}
kld[i]|=kld[];
}
frog[]=;
for(rg int i=;i<=upceil;++i) {
for(rg int j=;j<n;++j) if(i&(<<j)){
if(kld[i^(<<j)]&(<<j)) frog[i]+=frog[i^(<<j)];
}
}
printf("Case %d: %lld\n",++cnt,frog[upceil]);
}
} void clear() {
n=;
memset(kld,,sizeof kld);
memset(frog,,sizeof frog);
memset(gorf,,sizeof gorf);
}

Summary

在状压DP进行转移的时候,不一定需要枚举子集进行转移,常用的另一种转移方式是枚举集合中元素进行转移。

最新文章

  1. 关于iOS的runtime
  2. backup mysql
  3. BZOJ 4544: 椭圆上的整点
  4. CSS 3 3D转换
  5. 往sql数据库表中添加字段
  6. 实例讲解Linux下的makefile
  7. C++设计模式---职责链模式
  8. JavaScript的异步操作
  9. switch函数——Gevent源码分析
  10. php mysql_insert_id() 获取为空
  11. HDU 2094解题报告
  12. 第三节 - centos 内核启动、救援模式、 ls 、目录结构
  13. 验证码识别之w3cschool字符图片验证码(easy级别)
  14. redis+twemproxy实现redis集群
  15. 转摘app-稳定性测试
  16. day84-仿照admin实现一个自定义的增删改查组件
  17. jQuery中.html(“xxx”)和.append(&quot;xxx&quot;) 的区别
  18. Delphi7/2007/2009/2010/XE/XE2/XE3/XE4/XE5/XE6/XE7/XE8/10最终版
  19. svn常见错误解决
  20. org.jeecgframework.core.common.exception.MyExceptionHandler]java.lang.NullPointerException

热门文章

  1. Error -26377: No match found for the requested parameter
  2. Win10系统XWware虚拟机安装Linux系统(Ubuntu)最新版教程
  3. NO.01---今天聊聊Vuex的简单入门
  4. leetcode-前K个高频元素
  5. HTMLTestRunner解决UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe5 in position 108: ordinal not in range(128)
  6. Centos7 下nginx nginx-1.13.4 安装
  7. java通过控制鼠标实现屏幕广播
  8. c# 删除word文档中某一页
  9. Swift-assert使用时机
  10. bootstrap控件点击之后没有反应的原因