本文出自:http://blog.youkuaiyun.com/dr5459
题目地址:
http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2895
题目意思:
告诉你初始时可以杀掉那些机器人
你每杀掉一个机器人,就可以拿起他的武器,从而可以杀掉他的武器可以杀掉的机器人
问你杀掉全部的机器人有多少种方案
解题思路:
我们用集合S来表示杀了的机器人,二进制的1即为干掉了,0即为没干掉
那么用dp[s]就可以表示状态s的种数
那么怎么来转移呢?
首先,对于每一种状态,我们预处理一下这个状态下可以干掉哪些机器人,这样才能转移
然后对于一个状态S来说,要干掉第i个机器人,则要从没干掉i的状态里面转移,即s^(1<<i)
则dp[s] = sum(dp[s^(1<<i)]) 条件为s&(1<<i) 而且s^(1<<i)可以干掉i
最后加起来就OK啦
代码:
<pre name="code" class="cpp">#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 17;
int attack[maxn]; //表示每个机器人能干掉的
int can_attack[1<<16]; //表示每个状态能干掉的
long long dp[1<<16]; //用来统计每种状态的种数
char s[maxn];
int n;
int main()
{
int T;
int ca = 1;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
{
attack[i] = 0;
scanf("%s",s);
for(int j=0;j<n;j++)
{
if(s[j]=='1')
attack[i] |= (1<<j);
}
}
int total = (1<<n)-1;
for(int st=0;st<=total;st++)
{
can_attack[st] = attack[0];
//对每个机器人来说,如果状态可以干掉i,那么也就是可以干掉i所能干掉的
for(int i=1;i<=n;i++)
{
int j=i-1;
if(st&(1<<j))
can_attack[st] |= attack[i];
}
}
//枚举状态
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int st=1;st<=total;st++)
{
for(int i=0;i<n;i++)
{
if(st & (1<<i))//如果st的这种状态能够干掉i,
//那么要由不能干掉i的状态转移过来,即st^(1<<i)
//而且st^(1<<i)这种状态要能够干掉i
{
if(can_attack[st^(1<<i)]&(1<<i))
dp[st] += dp[st^(1<<i)];
}
}
}
cout<<"Case "<<ca++<<": "<<dp[total]<<endl;
}
return 0;
}