• 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;
    }
    
    
  • 相关阅读:
    2014.3.3 图像旋转方法
    2014.2.23 datagridview显示图片的方法
    2016.10.8 文件读取和两种模式写入
    2016.8.11 DataTable合并及排除重复方法
    2016.8.17服务器端数据库用户导入导出方法 expdp和impdp
    2016.8.11 禁用360进程防护功能
    2016.7.27 VS搜索正则表达式,在UltraEdit中可选用Perl正则引擎,按C#语法搜索
    2016.6.18主窗体、子窗体InitializeComponent()事件、Load事件发生顺序以及SeleChanged事件的发生
    delphi之猥琐的webserver实现
    HTTP协议中GET、POST和HEAD的介绍
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822501.html
Copyright © 2020-2023  润新知