[NOI2001] 炮兵阵地
[NOI2001] 炮兵阵地
状压 \(DP\) 板子题.
之前我没学状压的时候感觉这个题好难, 尽管现在我也没学, 却感觉这个题很简单, 可能这就是成长了吧...
设 \(f[i][j][k]\) 表示第 \(i\) 行状态为 \(k\) , 第 \(i - 1\) 行状态为 \(j\) 的最大士兵数量.
显然, 只要判一下上下左右两格没有重的就行了, 太简单了不说了.
这里有一点需要注意, 要开滚动数组, 否则会 \(MLE\) .
\(code:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
const int mod = 1e8;
int n, m, f[2][1 << 10][1 << 10], s[105], a[1 << 10], ans;
bool ok[1 << 10];
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
char ch;
scanf(" \n%c", &ch);
s[i] <<= 1;
if (ch == 'H') s[i] += 1;
}
}
for (int i = 0; i < (1 << m); i++) {
for (int j = i; j; j >>= 1) {
if (j & 1) a[i]++;
}
if (i << 1 & i || i << 2 & i || i >> 1 & i || i >> 2 & i) ok[i] = 1;
}
int o = 0, p = 1;
for (int i = 1; i <= n; i++) {
o ^= 1, p ^= 1;
for (int j = 0; j < (1 << m); j++) {
if (ok[j] || (i > 1 && s[i - 2] & j)) continue;
for (int k = 0; k < (1 << m); k++) {
if (j & k || ok[k] || s[i - 1] & k) continue;
for (int l = 0; l < (1 << m); l++) {
if (j & l || k & l || s[i] & l || ok[l]) continue;
f[o][k][l] = max(f[o][k][l], f[p][j][k] + a[l]);
ans = max(ans, f[o][k][l]);
}
}
}
}
printf("%d", ans);
return 0;
}
我们虽然写了 \(4\) 层循环, 但是其实每层没有 \(2^m\) 这么多, 因为我们隔两个才能放一个, 所以其实复杂度完全 \(ok\) 的.
看不见我看不见我看不见我