[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\) 的.

posted @ 2021-09-06 19:06  sshadows  阅读(31)  评论(0编辑  收藏  举报