非常好的题目
题意:给定一个初始武器,可以消灭编号1-i的人
每消灭一个机器人,获得其武器。
求总共可以有多少种方法消灭所有的机器人
状态描述: State[i] 在已经消灭了i二进制表示的机器人的情况下,对应的武器的情况
以第三组数据为例 State[0] 表示在0 0 0 即三个机器人都没有消灭的情况下 state[0] == 3 即 0 1 1 可以消灭到
第1个或者第2个机器人
每个状态i只和其前面的状态有关(非常重要),所以状态转移方程如下
dp[i] = 将i的某一位1的二进制位置为0以后且满足该State可以转移到目前状态 的 dp[i-1]...dp[0]的和
结论为dp[(1 << n) - 1] 即 所有的敌人都被消灭置为1的数目和
#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;
///宏定义
const int INF = 20000000;
const int maxn = 1<<17;
///全局变量 和 函数
//
int T;
int n;
long long dp[maxn];
int weapons[maxn];
int State[maxn];
char s[30];
int main()
{
//
int i, j;
scanf("%d", &T);
int cases = 1;
while (T--)
{
memset(dp, 0, sizeof(dp));
memset(weapons, 0, sizeof(weapons));
memset(State, 0, sizeof(State));
scanf("%d", &n);
for (i = 0; i <= n; i++)
{
scanf("%s", s);
for (j = 0; j < strlen(s); j++)
{
if ((s[j] - '0') != 0)
{
weapons[i] |= (1 << j);
}
}
}
State[0] = weapons[0];
for (i = 0; i < (1 << n); i++)
{
State[i] = State[0];
for (j = 0; j < n; j++)
{
//如果在本状态中第j个人被杀,取得其武器
if ((1 << j) & i)
{
State[i] |= weapons[j + 1];
}
}
}
dp[0] = 1;
for (i = 1; i < (1 << n); i++)
{
dp[i] = 0;
for (j = 0; j < n; j++)
{
//如果当前位置为1,说明该位置机器人已经被消灭,把该位置放置为0,并且查看该状态是否可以转移到此状态
//i ^ (1 << j) 把当前状态对应1返回为0, 取state数组查看该状态是否可以转移(即杀到当前位置机器人人)
// 所以 & (1 << j) 查看对应位置是否为1
if ( (i & (1 << j)) && (State[i ^ (1 << j)] & (1 << j)) )
{
dp[i] += dp[i ^ (1 << j)];
}
}
}
printf("Case %d: %lld\n", cases++, dp[(1 << n) -1]);
}
return 0;
}