Luogu 2704 [NOI2001]炮兵阵地

唔,想到了状压之后就不会了……实在是菜

考虑压两行,设$f_{i, j, k}$表示当前到第$i$行,上一行是$j$状态,前一行是$k$状态的最多能放的炮兵的数量。

发现第一维还可以滚掉,好像可以转移了。

但是,这样子$100 * 1024 * 1024 * 1024$时间炸了……怎么办?

考虑先不考虑题目中给出的不能放的格子的贡献,预处理出所有可行解,根据互不侵犯的套路,对于一个状态$i$,在这题中只要满足$i \& (i >> 1)$、$i \& (i >> 2)$、$i \& (i  << 1)$、$i \& (i << 2)$全部都是$0$,那么就是一个合法状态了吧。

对于一个合法状态(记编号为$i$),只要计算出有多少个$1$就相当于可以放多少个炮兵了吧,记这个数量为$num_i$。

一行最多才$10$列,一个炮兵可以打$5$格,发现预处理之后只有$60$来个有效状态了(状态数记为$tot$),$O(ntot^3)$可以承受了。

写一下转移方程:

    $f_{1, i, 0} = num_i$。

    $f_{2, i, j} = max(f_{1, j, 0} + num_i)$  $i, j$不冲突且$i$和第二行的地图不冲突。

    $f_{i, j, k} = max(f_{i - 1, k, t} + num_j)$  $j,k$ 、$k, t$、$j, t$不冲突且$j$和第$i$行的地图不冲突。

答案  $ans = max(f_{n, i, j})$。

注意第二行要分开处理。

时间复杂度$O(ntot^3)$,约为$O(n^4)$。

以后要记得复习!

Code:

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 105;

int n, m, a[N], tot = 0, b[N], num[N], f[N][N][N];

inline void chkMax(int &x, int y) {
    if(y > x) x = y;
}

inline int bitCnt(int now) {
    int res = 0;
    for(; now > 0; now >>= 1)
        res += (now & 1);
    return res;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) {
        char str[15];
        scanf("%s", str + 1);
        for(int j = 1; j <= m; j++)
            if(str[j] == 'H') a[i] |= (1 << (j - 1));
    }

    for(int i = 0; i < (1 << m); i++)
        if(!(i & (i << 1)) && !(i & (i << 2)) && !(i & (i >> 1)) && !(i & (i >> 2))) {
            ++tot;
            b[tot] = i, num[tot] = bitCnt(i);
            if(!(a[1] & b[tot])) f[1][tot][0] = num[tot];
        }
    
    for(int i = 1; i <= tot; i++)
        for(int j = 1; j <= tot; j++)
            if(!(a[2] & b[j]) && !(b[i] & b[j]))
                chkMax(f[2][j][i], f[1][i][0] + num[j]);
    
    for(int i = 3; i <= n; i++)
        for(int j = 1; j <= tot; j++) {
            if(a[i] & b[j]) continue;
            for(int k = 1; k <= tot; k++) {
                if(b[j] & b[k]) continue;
                for(int t = 1; t <= tot; t++) {
                    if((b[k] & b[t]) || (b[t] & b[j])) continue;
                    chkMax(f[i][j][k], f[i - 1][k][t] + num[j]);
                }
            }
        }
    
    int ans = 0;
    for(int i = 1; i <= tot; i++)
        for(int j = 1; j <= tot; j++)
            chkMax(ans, f[n][i][j]);
    
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2018-10-20 21:13  CzxingcHen  阅读(163)  评论(0编辑  收藏  举报