zoukankan      html  css  js  c++  java
  • 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
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    RecyclerView,ListView first item get focus
    一种粗暴快速的Android全屏幕适配方案(转)
    谈谈单元测试之(一):为什么要进行烦人的单元测试(转)
    SCIgen与野鸡期刊的梗
    百度搜索仍然是【最快解决工作问题的方法】
    搭建minima主题的github博客网站
    本科期间的第一个专利:改卷筒的故事
    学习消费思维的【贫穷与富有】
    我的联系方式
    学习 GitHub 有什么好处?
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9822970.html
Copyright © 2011-2022 走看看