#LOJ2104 TJOI2015棋盘 状压dp 矩阵快速幂优化dp

版权声明:_ https://blog.csdn.net/lunch__/article/details/81951041

题目链接

其实这道题做的我挺难受的…. 看不太懂该怎么搞.网上的东西也都讲的很简单

首先这个题发现 m 很小,考虑把每一行状压下来

预处理出哪一行能转移到哪一行

这个东西我不太会搞,还是看的 L S T e t e 的代码…

就是你对于当前枚举的两个状态,每找到一个 1 ,把棋子的位置强行平移到这个 1
的位置上来, 然后判一下相邻的状态就好了

直接做发现 1 e 6 的数据范围是过不了的,我们考虑优化,发现每一行转移的都是相同的。通俗一点可以这样讲,如果状态 S 的后继状态集合为 S 2 , 那么无论是哪一行的状态为 S 时,它都能够转移到 S 2 的每一个状态中,这样子就可以用矩阵快速幂优化了,考虑怎么构造转移矩阵,其实预处理的时候就可以直接构造好了,如果 i 状态能转移到 j 状态,那么转移矩阵 ( i , j ) 这个位置对应的系数就是 1

复杂度 O ( 2 3 m l o g n )

Codes

#include<bits/stdc++.h>

#define int unsigned int

using namespace std;

const int N = (1 << 6) + 3;

int n, m, p, k;
int a[N], all, ans;

struct Martix {
    int A[N][N];
    Martix operator * (const Martix &T) {
        Martix res;
        for(int i = 0; i < all; ++ i)
            for(int j = 0; j < all; ++ j) {
                res.A[i][j] = 0;
                for(int q = 0; q < all; ++ q)
                    res.A[i][j] += A[i][q] * T.A[q][j];
            }
        return res;
    }
}A1, A2;

void qpow(int x) {
    while(x) {
        if(x & 1) A2 = A2 * A1;
        x >>= 1, A1 = A1 * A1;
    }
}

bool check(int S1, int S2) {
    int pre, now, sub;
    for(int i = 0; i < m; ++ i) {
        if((1 << i) & S1) {
            if(i > k) now = a[1] << (i - k), sub = a[2] << (i - k);
            else now = a[1] >> (k - i), sub = a[2] >> (k - i);
            if((S1 & now) || (S2 & sub)) return false;
        }
        if((1 << i) & S2) {
            if(i > k) now = a[1] << (i - k), pre = a[0] << (i - k);
            else now = a[1] >> (k - i), pre = a[0] >> (k - i);
            if((S2 & now) || (S1 & pre)) return false;
        }
    }
    return true;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("3977.in", "r", stdin);
    freopen("3977.out", "w", stdout);
#endif
    scanf("%u%u%u%u", &n, &m, &p, &k);
    for(int i = 0, x; i < 3; ++ i) 
        for(int j = 0; j < p; ++ j) 
            scanf("%d", &x), a[i] |= x << j;
    all = 1 << m; a[1] ^= 1 << k;
    for(int i = 0; i < all; ++ i)
        for(int j = 0; j < all; ++ j)
            A1.A[i][j] = check(i, j);
    for(int i = 0; i < all; ++ i)
        A2.A[i][i] = 1;
    qpow(n);
    for(int i = 0; i < all; ++ i)
        ans += A2.A[0][i];
    cout << ans << endl;
    return 0;
}

位运算相关的东西自己知道的还是少了点啊…
以后这种题还是独立做下吧..

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/81951041