BZOJ1801 [Ahoi2009]chess 中国象棋 [动态规划]

中国象棋

题目描述见链接 .


color{grey}{最初想法}

  • 可以发现每行每列最多只能够放 22 个棋子 .

F[i,j,a,b]F[i, j, a, b] 表示前 ii 行,前 jj 列,第 ii 行放置了 aa 个棋子, 第 jj 列放置 bb 个棋子的方案数, 发现不可行.

color{red}{正解部分}

  • 发现可以不用关心棋子的顺序 .

F[i,j,k]F[i, j, k] 表示前 ii 行, 有 jj 列放了 11 个棋子, kk 列放了 22 个的方案数,

为什么这样设状态? 因为这样设状态可以方便的得知放 00 个棋子的列数是 MjkM-j-k .

考虑按行转移:

  • ii 行放 11 个棋子, #color{red}{#}
    F[i,j,k]=F[i1,j+1,k1]+F[i1,j1,k]F[i, j, k] = F[i-1, j+1,k-1] + F[i-1,j-1,k] .

  • ii 行放 22 个棋子, #color{red}{#}
    F[i,j,k]=F[i1,j2,k]+F[i1,j,k1]+F[i,j+2,k2]F[i, j, k] = F[i-1,j-2,k]+F[i-1,j,k-1]+F[i,j+2,k-2] .

  • 不放棋子, (这个容易忘记) .
    F[i,j,k]=F[i1,j,k]F[i, j, k] = F[i-1, j, k] .


到这里, 相信已经有人发现了不对的地方, 上面标红色井号的方程是不是显得有些太简单了 …
原来是状态的转移没考虑到全部情况, 即有多种方式从 i1i-1 行的状态到 ii 行状态,
下面重新写一遍状态转移方程,

  • ii 行放 11 个棋子, #color{red}{#}
    F[i,j,k]=(j+1)F[i1,j+1,k1]+(Mj+1k)F[i1,j1,k]F[i, j, k] = (j+1)*F[i-1, j+1,k-1] + (M-j+1-k)*F[i-1,j-1,k] .

  • ii 行放 22 个棋子, #color{red}{#}
    F[i,j,k]=CMj+2k2F[i1,j2,k]+(Mjk+1)jF[i1,j,k1]+Cj+22F[i,j+2,k2]F[i, j, k] = C_{M-j+2-k}^{2}F[i-1,j-2,k] + (M-j-k+1)*j*F[i-1,j,k-1] + C_{j+2}^2F[i,j+2,k-2] .

  • 不放棋子, (这个容易忘记) .
    F[i,j,k]=F[i1,j,k]F[i, j, k] = F[i-1, j, k] .

最后答案即为 F[N,i,j]sum F[N, i, j] .


color{red}{实现部分}

  • 初值 F[0,0,0]=0F[0, 0, 0] = 0 .
  • 由于要用到的组合数 mm 都等于 22, 于是直接计算即可 .
#include<bits/stdc++.h>

const int mod = 9999973;
const int maxn = 105;

typedef long long LL;

int N, M;
int dp[maxn][maxn][maxn];

int C(int m){ return m*(m-1) >> 1; }

int main(){
        scanf("%d%d", &N, &M);
        dp[0][0][0] = 1;
        for(int i = 1; i <= N; i ++)
                for(int j = 0; j <= M; j ++)
                        for(int k = 0; k <= M; k ++){
                                dp[i][j][k] = dp[i-1][j][k];
                                if(k-1 >= 0) dp[i][j][k] = ((LL)dp[i][j][k] + dp[i-1][j+1][k-1] * (j+1)) % mod;
                                if(j-1 >= 0 && (M-(j-1)-k)) dp[i][j][k] = ((LL)dp[i][j][k] + (LL)dp[i-1][j-1][k] * (M-(j-1)-k) % mod) % mod;
                                if(j-2 >= 0 && (M-(j-2)-k)) dp[i][j][k] = ((LL)dp[i][j][k] + (LL)dp[i-1][j-2][k] * C(M-(j-2)-k) % mod) % mod;
                                if(k-1 >= 0 && (M-j-(k-1))) dp[i][j][k] = ((LL)dp[i][j][k] + (LL)(dp[i-1][j][k-1] * (M-j-(k-1)) % mod)* j % mod) % mod;
                                if(k-2 >= 0) dp[i][j][k] = ((LL)dp[i][j][k] + (LL)dp[i-1][j+2][k-2] * C(j+2) % mod) % mod;
                        }
        int Ans = 0;
        for(int j = 0; j <= M; j ++)
                for(int k = 0; k <= M; k ++)
                        Ans = ((LL)Ans + dp[N][j][k]) % mod;
        printf("%d", Ans);
        return 0;
}

原文地址:https://www.cnblogs.com/zbr162/p/11822501.html