P2051 中国象棋

P2051 中国象棋


Dp题
一开始的我是考虑状呀的。 然后发现。
voc,怎么n和m是100的。然后果断GG

想了许久。然后发现,这个炮只是对他所在的行与列产生影响,对于其他的行与列来说并没有什么影响。

而一行中的炮,我们可以一次把他干出来。

所以影响我们的决策的时候,只有列上的情况对我们有影响。

然后又因为不同列之间互不影响,然后我们只需要记录每种情况的个数就Ok了

我们考虑设计如下状态
(f[i],[j],[k])表示当前我们到了第i行,有(j)个列上有一个炮,有(k)个列上有两个炮。

考虑转移

第i行不放炮
(f[i][j][k]+=f[i-1][j][k])

第i行放一个炮
1.放在没有炮的列上
(f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1))
2.放在一个炮的列上
(f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))

第i行放两个炮
1.都放在没有炮的列上
(f[i][j][k]+=f[i-1][j-2][k]*C^2_{m-j-k+2})
2.都放在一个炮的列上
(f[i][j][k]+=f[i-1][j+2][k-2]*C^2_{j+2})
3.一个放在一个炮的列上,另一个放在没有炮的列上
(f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1))

乘法原理和加原理搞一搞就可以了

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
const int maxn=110;
long long f[maxn][maxn][maxn];
long long mode(long long val)
{
    return val%9999973;
}
long long C(int T)
{
    return mode(T*(T-1)/2);
}
int main()
{
    f[0][0][0]=1;
    int n,m;
    scanf("%d%d",&n,&m);
    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]=mode(f[i][j][k]+f[i-1][j][k]);//注意判断边界
                if(j-1>=0)  f[i][j][k]=mode(f[i][j][k]+f[i-1][j-1][k]*(m-j+1-k));
                if(j-2>=0)  f[i][j][k]=mode(f[i][j][k]+f[i-1][j-2][k]*C(m-j+2-k));
                if(k-1>=0)  f[i][j][k]=mode(f[i][j][k]+f[i-1][j][k-1]*j*(m-j-k+1));
                if(k-1>=0&&j+1<=m)  f[i][j][k]=mode(f[i][j][k]+f[i-1][j+1][k-1]*(j+1));
                if(k-2>=0&&j+2<=m)  f[i][j][k]=mode(f[i][j][k]+f[i-1][j+2][k-2]*C(j+2));
            }
    long long ans=0;
    for(int i=0;i<=m;i++)
        for(int j=0;i+j<=m;j++)
            ans=mode(ans+f[n][i][j]);
    printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/Lance1ot/p/9810051.html