/* 返回顶部 */

Luogu P2704 [NOI2001]炮兵阵地

gate

还是状压dp...

因为int开成bool了de了好久,最后还是wjh帮忙看出来的qaq

 

f[i][k][j]代表第i行,状态为j,上一行状态为k,上上行的状态为kk

四重循环,保证j,k、j,kk、k,kk不冲突的前提下,有:

f[i][k][j] = max(f[i][kk][k] + sum[j])

因为状态太多了存不下,所以只要把可行的状态记录下来,也就是说离散化一下w

据说还可以用滚动数组,只记录两行的(f[i%2][j][k])不过空间反正足够了

前两行初始化要稍微注意下。

 

一点以后可能有用的优化:

1.这个题“只能放在平原上,不能放在山地上”,我原来是写完全包含 (g[j]|a[i]) == a[i]

后来看题解a[i]存的是山地的位置,也就是  !(g[j]&a[i])

2.这个题是横着比较短而竖着比较长而爆空间的…

如果遇到竖着短横着长的情况,不妨把它旋转一下。这样枚举状态的循环就会变得短一些!

 

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

char s[15];
int n,m,ans,cnt;
int a[1<<11],sum[70],f[105][70][70],g[70];

int main() {
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) {
        scanf("%s",s);
        for(int j = 0; j < m; j++)
            a[i] = (a[i] << 1)|(s[j] == 'H');
    }
    for(int i = 0; i < (1<<m); i++) {
        if(i&(i<<2)||i&(i<<1)||i&(i>>1)||i&(i>>2)) continue;
        g[++cnt] = i;
        for(int j = 0; i>>j; j++)
            sum[cnt] += (i>>j)&1;
        if(g[cnt]&a[1]) continue;
        f[1][0][cnt] = sum[cnt];
    }
    for(int i = 1; i <= cnt; i++)
        for(int j = 1; j <= cnt; j++) {
            if(g[j] & a[2]) continue;
            if(g[j] & g[i]) continue;
            f[2][i][j] = max(f[2][i][j],f[1][0][i] + sum[j]);
        }

    for(int i = 3; i <= n; i++)
        for(int j = 1; j <= cnt; j++) {
            if(g[j]&a[i]) continue;
            for(int k = 1; k <= cnt; k++)
                for(int kk = 1; kk <= cnt; kk++) {
                    if(g[j]&g[k] || g[j]&g[kk] || g[k]&g[kk]) continue;
                    f[i][k][j] = max(f[i][k][j],f[i-1][kk][k]+sum[j]);
                    ans = max(ans,f[i][k][j]);
                }
        }
    printf("%d\n",ans);
    return 0;
}
View Code

 

posted @ 2019-10-21 13:56  Mogeko  阅读(113)  评论(0编辑  收藏  举报