P2051 [AHOI2009]中国象棋

题目链接
题目大意:
给定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);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值