题目链接
题目大意:
给定n*m大小表格,让你摆放棋子,摆放的要求是同一行、同一列中的棋子数不超过两个,问一共可能有多少摆法。
(不看别人的题解真不会开dp数组,太弱小了)
dp数组开三位,表示已经处理好了前i行时,有j列有一个棋子,有k列有两个棋子。
dp数组开完之后思路就很明了。
我们每次最多可以新摆放两个棋子(如果多了同一行的就会超出限制)
Ⅰ、先考虑摆放零个的情况:
直接就和上一层的一样了。
Ⅱ、摆一个的情况:
我们只能摆在有零个或者一个棋子的那一列。
Ⅲ、摆两个的情况:
还是可以摆在有零个或者一个棋子的那一列,不过需要进行组合,所以一共有三种摆法。
全部加起来就是六种了。
注意取模,计算过程中乘法可能爆
i
n
t
int
int,所以还是要
l
o
n
g
l
o
n
g
longlong
longlong
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 9999973;
const int maxn = 110;
ll dp[maxn][maxn][maxn];
//dp[i][j][k]表示已经处理了前i行
//其中有j列为1个炮,k列为2个炮
//所有的状态:
//1、啥都不干,等着摆烂
//2、放一个到没有放炮的列
//3、放一个到放了一个炮的列
//4、放俩,都放在没有放炮的列
//5、还是放俩,一个放在有一个炮的列,一个放在没炮的列
//6、仍然放俩,都放在有一个炮的列
inline ll C2(ll x)
{
return ((x * (x - 1) % mod) / 2) % mod;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
dp[0][0][0] = 1;
for (int i = 0;i < n;i++)
{
for (int j = 0;j <= m;j++)
{
for (int k = 0;j + k <= m;k++)
{
//没有炮的列:m-j-k
//有一个炮的列:j
//有俩:k
dp[i + 1][j][k] = (dp[i + 1][j][k] + dp[i][j][k]) % mod;
if (m - j - k >= 1)
dp[i + 1][j + 1][k] = (dp[i + 1][j + 1][k] + (m - j - k) * dp[i][j][k] % mod) % mod;
if (j >= 1)
dp[i + 1][j - 1][k + 1] = (dp[i + 1][j - 1][k + 1] + j * dp[i][j][k] % mod) % mod;
if (m - j - k >= 2)
dp[i + 1][j + 2][k] = (dp[i + 1][j + 2][k] + C2(m - j - k) * dp[i][j][k] % mod) % mod;
if (j >= 1 && (m - j - k >= 1))
dp[i + 1][j][k + 1] = (dp[i + 1][j][k + 1] + (j * (m - j - k) % mod) * dp[i][j][k] % mod) % mod;
if (j >= 2)
dp[i + 1][j - 2][k + 2] = (dp[i + 1][j - 2][k + 2] + C2(j) * dp[i][j][k] % mod) % mod;
}
}
}
ll ans = 0;
for (int j = 0;j <= m;j++)
{
for (int k = 0;j + k <= m;k++)
{
ans = (ans + dp[n][j][k]) % mod;
}
}
printf("%lld\n", ans);
}