本文出自:http://blog.csdn.net/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啦
代碼:
#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;}