[AHOI2009]中国象棋(luogu P2051)

原题链接:https://www.luogu.org/problem/show?pid=2051

本题是一道DP,思维难度主要是在多种状态的考虑上。

对于每一行,最多放两个炮,所以,对于每一行,只会有放一个炮,放两个炮或者不放炮的三种情况

以f[i][j][k]表示前i行中,有j列有1个炮,k列有两个炮,则有m-j-k列没有炮,a[i]记录C(i,2)。

1.不放炮:直接将本行的方案数加到下一行即可。

2.放一个炮,放在一个空列上。要注意空行有m-j-k个,所以要乘上m-j-k再加到下一行

3.放一个炮,放在一个有一个炮的列上。

尤其要注意,此时可能没有任何一列只有一个炮的,所以要特判能否进行此次转移。

4.放两个炮,都放在空列上。空列有(m-j-k)个,而从中选两个放炮,就有C(m-i-j,2)种选法。

5.放两个炮,分别放在一个空列上和一个只有一个炮的列上,其实相当于在一个空列上放了两个炮,空列有(m-j-k)个,只有一个炮的列有j个。

6.放两个炮,都放在只有一个炮的列上,首先特判j>=2时才进行转移,有C(j,2)种选法。

由此,我们就能得到六个转移方程(未特判):

 

f[i+1][j][k]+=f[i][j][k];
f[i+1][j+1][k]+=((f[i][j][k])*(m-k-j))%c;
f[i+1][j-1][k+1]+=(f[i][j][k]*j)%c;
f[i+1][j+2][k]+=(f[i][j][k]*a[m-j-k])%c;
f[i+1][j][k+1]+=(f[i][j][k]*(m-j-k)*j)%c;
f[i+1][j-2][k+2]+=(f[i][j][k]*a[j])%c;

 

转移完成后统计答案,f[i][j][k]中j+k<=m的方案符合要求,加入答案中(j+k>m的情况显然是不合实际的)

 

#include<cstdio>
const int c=9999973; 
long long ans,f[105][105][105];//前i行j 1,k 2 m-i-j 0
int n,m,a[105];
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=2;i<=100;i++) a[i]=(i*(i-1))/2;
    f[0][0][0]=1;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            for(int k=0;k<=m;k++)
            {
                f[i][j][k]%=c;
                f[i+1][j][k]+=f[i][j][k];
                f[i+1][j+1][k]+=((f[i][j][k])*(m-k-j))%c;
                if(j>0) f[i+1][j-1][k+1]+=(f[i][j][k]*j)%c;
                f[i+1][j+2][k]+=(f[i][j][k]*a[m-j-k])%c;
                f[i+1][j][k+1]+=(f[i][j][k]*(m-j-k)*j)%c;
                if(j>1) f[i+1][j-2][k+2]+=(f[i][j][k]*a[j])%c;
            }
        }
    }
    for(int i=0;i<=m;i++)
    {
        for(int j=0;i+j<=m;j++) ans=(ans+f[n][i][j])%c;
    }
    printf("%lld",ans);
    return 0;
}

 

posted @ 2017-10-18 14:03  Excim  阅读(157)  评论(0编辑  收藏  举报