BZOJ 1801 [Ahoi2009]中国象棋(线性动规)(洛谷P2051)

题意:就是在n*m的格子中放“炮”(中国象棋中的棋子)问有多少种放法,使得没有任意的两个炮相互攻击

思路:我们很容易的得到一列或者一行中最多放下两个炮(我也只能得到这些了,满脑子状压,但数据范围有100),这篇博客些的很好(传送门),我们定义dp[i][j][k]代表前i行我们有j列式有2个棋子,有k列是一个棋子,那么我们空的列的个数为m-i-j,我们在每行填的时候最多填2个,这样保证了我们每行不会多于2个。然后就是分情况讨论了

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const LL MOD=9999973;
LL dp[107][107][107];

int main()
{
    int n=read(),m=read();
    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++){
                LL res=0;
                res+=dp[i-1][j][k];
                if(j)res+=dp[i-1][j-1][k+1]*(k+1);
                if(k)res+=dp[i-1][j][k-1]*(m-j-k+1);
                if(j&&k)res+=dp[i-1][j-1][k]*k*(m-j-k+1);
                if(j>1)res+=dp[i-1][j-2][k+2]*(k+2)*(k+1)/2;
                if(k>1)res+=dp[i-1][j][k-2]*(m-j-k+2)*(m-j-k+1)/2;
                dp[i][j][k]=res%MOD;
            }
        }
    }
    LL ans=0;
    for(int i=0;i<=m;i++){
        for(int j=0;j<=m-i;j++){
            ans=(ans+dp[n][i][j])%MOD;
        }
    }
    printf("%lld\n",ans%MOD);
    return 0;
}

 

posted @ 2018-10-15 15:10  啦啦啦天啦噜  阅读(132)  评论(0编辑  收藏  举报