luogu2704[NOI2001]炮兵阵地

题目链接
因为m <= 10,考虑状压DP
先dfs搜出所有可能状态
f[i][j][k] 表示上上行是 i, 上行是 j, 第 k 行取得的最大值
但如果直接开满数组,MLE
考虑滚动数组
此题只需保留2个状态,上上行,上行
所以用四层循环枚举——超时
考虑剪枝
放进上上行时先与题述条件对比
放入上行是与上上行和题述条件进行比较
。。。。。。
因为这个DP数组单调递增,不用担心之前的发生冲突,不放心可以开一个used数组
最后输出,312ms,不慢

#include<bits/stdc++.h>
using namespace std;

const int N = 1 << 10 | 1;

int f[N][N][3]; // f[i][j][k] 表示上上行是 i, 上行是 j, 第 k 行取得的最大值 
int g[N], v[N];
int o[101];// 每一行本身的限制 
int tot = 0;
int n, m;

void dfs(int u, int now, int cur) {
    if(u > m) {
        g[++tot] = now;
        v[tot] = cur;
        return;
    }
    dfs(u + 1, now, cur);
    dfs(u + 3, now + (1 << (u - 1)), cur + 1);
}

int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            char c;
            cin >> c;
            if(c == 'H') o[i] += 1 << (j - 1);
        }
    }
    dfs(1, 0, 0);
    for(int i = 1; i <= tot; i++) {
        if((g[i] & o[1]) == 0) {
            f[0][g[i]][1] = v[i];
        }
    }
    for(int i = 2; i <= n; i++) {
        for(int j = 1; j <= tot; j++) {// 上上行可能的放置方法 
            if(g[j] & o[i - 2]) continue;
            for(int l = 1; l <= tot; l++) {// 上行可能的放置方法 
                if((g[l] & o[i - 1]) || (g[l] & g[j])) continue;
                for(int k = 1; k <= tot; k++) {
                    if((g[k] & o[i]) || (g[k] & g[j]) || (g[k] & g[l])) continue;
                    f[g[l]][g[k]][i % 3] = max(f[g[l]][g[k]][i % 3], f[g[j]][g[l]][(i - 1) % 3] + v[k]);
                }
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= tot; i++) {
        for(int j = 1; j <= tot; j++) {
            /*for(int l = 1; l <= 2; l++) {
                printf("f[%d][%d][%d] = %d, ", g[i], g[j], l, f[g[i]][g[j]][l]);
            }*/
            ans = max(ans, f[g[i]][g[j]][n % 3]);
        }
    }
    printf("%d", ans);
    return 0;
} 
阅读更多

没有更多推荐了,返回首页