P2704 [NOI2001]炮兵阵地

P2704 [NOI2001]炮兵阵地

题目链接

​ 状压DP。

​ 这个炮可以打到上面两行,这个点卡了我很久,我一开始就压一行的状态,发现会无线套娃:当前行可能会打到上两行,你还不能只判断当前行与上一行是否匹配,还得判断上两行;判了上一行还要判断上一行的上两行。。。。

​ 为了解决这个问题,我们可以直接把两行的状态放到一个数组里,这样问题就解决了。

​ 转移方程:\(dp[i][F][G] = max(calc(F) + dp[i - 1][G][LG])\)\(F\)为当前行的状态,\(G\)为上一行的状态,\(LG\)为上两行的状态, \(calc(F)\)是计算当前行的炮兵数。

#include <bits/stdc++.h>
    
using namespace std;
    
inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}
    
const int N = 105, M = 2000;
int n, m, ans;
int num[N], c[N][M], S[N][M], mat[N][N], dp[N][M][M];

int judge(int x, int s) {
    if((s << 1) & s) return 0;
    if((s << 2) & s) return 0;
    if((s >> 1) & s) return 0;
    if((s >> 2) & s) return 0;
    for(int i = 0;i < m; i++) {
        int tmp = (s >> i) & 1;
        if(mat[x][m - i] == 0 && tmp == 1) return 0;
    }
    return 1;
}

void make_pre_S() {
    for(int i = 1;i <= n; i++) 
        for(int j = 0;j <= (1 << m) - 1; j++) 
            if(judge(i, j)) S[i][++num[i]] = j;
}

int calc(int s) {
    int res = 0;
    for(int i = 0;i < m; i++) 
        if((s >> i) & 1) res++;
    return res;
}

int match(int s1, int s2) {
    if(s1 & s2) return 0;
    return 1;
}

int main() {

    n = read(); m = read();
    for(int i = 1;i <= n; i++) {
        string ch; cin >> ch;
        for(int j = 1;j <= m; j++) 
            if(ch[j - 1] == 'P') mat[i][j] = 1;
    }

    make_pre_S(); // 预处理出所有合法状态
    num[0] = 1;
    for(int i = 1;i <= num[1]; i++) 
        dp[1][S[1][i]][0] = calc(S[1][i]);
      for(int i = 2;i <= n; i++) {
        for(int s1 = 1;s1 <= num[i]; s1++) {
            int F = S[i][s1];
            for(int s2 = 1;s2 <= num[i - 1]; s2++) {
                int G = S[i - 1][s2];
                for(int s3 = 1; s3 <= num[i - 2]; s3++) {
                    int LG = S[i - 2][s3];
                    if(match(F, G) && match(F, LG) && match(G, LG)) {
                        dp[i][F][G] = max(dp[i][F][G], calc(F) + dp[i - 1][G][LG]);
                    }
                }
            }
        }
    }

    for(int s1 = 1;s1 <= num[n]; s1++) {
        int F = S[n][s1];
        for(int s2 = 1; s2 <= num[n - 1]; s2++) {
            int G = S[n - 1][s2];
            ans = max(ans, dp[n][F][G]);
        }
    }

    printf("%d", ans);
    

    return 0;
}

posted @ 2020-09-09 20:42  C锥  阅读(134)  评论(0编辑  收藏  举报