AHOI2009 中国象棋

题目链接

Solution

根据题目规则,很显然,每一行每一列最多只能放两个棋子。设 (f_{i,j,k}) 表示处理到第 (i) 行,有 (j) 列放了一个棋子,(k) 列放了两个棋子的方案数。分情况讨论第 (i) 行的棋子放置情况:

什么也不放

直接转移即可:

[f_{i,j,k}=f_{i-1,j,k} ]

放一个棋子

有两种情况:

  1. 放在没有棋子的一列

[f_{i,j,k}+=f_{i-1,j-1,k}*(m-j-k+1) ]

  1. 放在有一个棋子的一列

[f_{i,j,k}+=f_{i-1,j+1,k-1}*(j+1) ]

放两个棋子

有三种情况:

  1. 都放在没有棋子的列上

[f_{i,j,k}+=f_{i-1,j-2,k}*((m-j-k+2)*frac{m-j-k+1}{2}) ]

  1. 都放在有一个棋子的列上

[f_{i,j,k}+=f_{i-1,j+2,k-2}*((j+2)*frac{j+1}{2}) ]

  1. 一个放在没有棋子的列上,一个放在有一个棋子的列上

[f_{i,j,k} += f_{i-1,j,k-1}*j*(m-j-k+1) ]

状态转移方程就写完了,注意判断边界。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
const int N = 233, mod = 9999973;
long long n, m, f[N][N][N], ans = 0;
 
int main() 
{
    scanf("%lld%lld", &n, &m);
    f[0][0][0] = 1;
    for(int i = 1; i <= n; i++)
    for(int j = 0; j <= m; j++)
    for(int k = 0; k + j <= m; k++)
    {
        f[i][j][k] += (f[i - 1][j][k]) % mod;
        if(k > 0) f[i][j][k] += (f[i - 1][j + 1][k - 1] * (j + 1)) % mod;
        if(j > 0) f[i][j][k] += (f[i - 1][j - 1][k] * (m - j - k + 1)) % mod;
        if(k > 0) f[i][j][k] += (f[i - 1][j][k - 1] * j * (m - j - k + 1)) % mod;
        if(j > 1) f[i][j][k] += (f[i - 1][j - 2][k] * ((m - j - k + 2) * (m - j - k + 1) / 2)) % mod;
        if(k > 1) f[i][j][k] += (f[i - 1][j + 2][k - 2] * ((j + 2) * (j + 1) / 2)) % mod;
        f[i][j][k] %= mod;
    }
    for(int i = 0; i <= m; i++) for(int j = 0; j <= m; j++)
        ans = (ans + f[n][i][j]) % mod;
    printf("%lld", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Andy-park/p/12508733.html