UVa 11795 - Mega Man's Mission

链接

传送门

题意

有1个初始武器,\(n (1 \leq n \leq 16)\)个敌人,击败每个敌人之后都会获得新的武器,每个武器所能击败的敌人也不同,给出每个武器能击败的敌人,输出最终击败所有敌人的方法总数。

思路

用二进制数表示当前状态。定义\(dp[i]、sta[i]\)分别表示击败敌人的集合为\(i\)时的方法总数和武器所能击败的敌人的集合。每个状态可以转移至击败一名新的敌人之后的状态,时间复杂度\(O(n2^n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

typedef long long LL;
const int maxn = 20;
const int maxm = (1 << 16) + 10;
char s[maxn];
int n, a[maxn];
int sta[maxm];
LL dp[maxm];

inline int read() {
scanf("%s", s);
int x = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == '1') {
x |= 1 << i;
}
}
return x;
}

int main() {
int t, tt = 0;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 0; i <= n; ++i) {
a[i] = read();
}
int S = 1 << n;
memset(dp, 0, sizeof dp);
memset(sta, 0, sizeof sta);
dp[0] = 1, sta[0] = a[0];
for (int i = 0; i < S; ++i) {
int k = sta[i] & (~i);
for (int j = 0; j < n; ++j) {
if (k & (1 << j)) {
dp[i | (1 << j)] += dp[i];
sta[i | (1 << j)] = sta[i] | a[j + 1];
}
}
}
printf("Case %d: %lld\n", ++tt, dp[S - 1]);
}
return 0;
}