洛谷P2051 [AHOI2009]中国象棋
运用数学知识递推。定义\(DP[i][j][k]\)为前i行内选择j个列只有一个炮,k列只有两个炮的放置方案总数。
因为如果有一列或者一行的炮大于等于三时,必会有一个炮会被攻击到,所以可以用一个和两个来区分,方便递推。这样可以使的列里面的炮不会大于等于三,然后考虑行,这一行内只能放两个或一个,所以可以考虑从这方面来推出递推公式。放的这一个或两个炮会改变列上的炮数,所以可以从上一行的炮的状态转移。
有递推公式:
dp[i][j][k] = (dp[i][j][k] + (j + 1) * dp[i - 1][j + 1][k - 1]) % mod;//j+1个列里任选一个即可有总共k个放了两个炮的列,同时j+1个放1个炮的列也变成了j个
dp[i][j][k] = (dp[i][j][k] + j * (m - (j+k-1) ) * dp[i - 1][j][k - 1]) % mod;//j列任选一个,空列任选一个, 都放一个,即可有k个放了两个炮的列乘法原理。
dp[i][j][k] = (dp[i][j][k] + (m - j - k + 1) * dp[i - 1][j - 1][k]) % mod//空列放一个,即可有j个放了一个炮的列。
dp[i][j][k] = (dp[i][j][k] + C(j + 2) * dp[i - 1][j + 2][k - 2]) % mod;//j+2列里任选两个。
dp[i][j][k] = (dp[i][j][k] + C(m - j - k + 2) * dp[i - 1][j - 2][k]) % mod;//空列里任选两个
Code:
#include <bits/stdc++.h>
#define int long long
const int mod = 9999973;
using namespace std;
int n, m, ans;
int dp[111][111][111];//三维数组dp[i][j][k]表示前i行内放了有j个只放了一个炮的列,k个放了两个炮的列的总方案数
int C(int n) {return ((n * (n - 1)) / 2) %mod;} //C(n, 2),n个数里面选择2个数的组合数。
signed main()
{
scanf("%lld%lld", &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 - j; k++)
{
dp[i][j][k] = dp[i][j][k] + dp[i - 1][j][k] % mod;
if (k >= 1)
dp[i][j][k] = (dp[i][j][k] + (j + 1) * dp[i - 1][j + 1][k - 1]) % mod;//j+1个列里任选一个即可有总共k个放了两个炮的列,同时j+1个放1个炮的列也变成了j个
if (k >= 1)
dp[i][j][k] = (dp[i][j][k] + j * (m - (j+k-1) ) * dp[i - 1][j][k - 1]) % mod;//j列任选一个,空列任选一个, 都放一个,即可有k个放了两个炮的列乘法原理。
if (j >= 1)
dp[i][j][k] = (dp[i][j][k] + (m - j - k + 1) * dp[i - 1][j - 1][k]) % mod//空列放一个,即可有j个放了一个炮的列。
if (k >= 2)
dp[i][j][k] = (dp[i][j][k] + C(j + 2) * dp[i - 1][j + 2][k - 2]) % mod;//j+2列里任选两个。
if (j >= 2)
dp[i][j][k] = (dp[i][j][k] + C(m - j - k + 2) * dp[i - 1][j - 2][k]) % mod;//空列里任选两个
}
for (int i = 0; i <= m; i++)
for (int j = 0; j <= m; j++)
ans += dp[n][i][j], ans %= mod;//全放两个的,全放一个的和放两个和放一个都有的,都有可能出现,所以都要加上;
printf("%lld", ans % mod);
return 0;
}