洛谷P2051 中国象棋

题意:

在一个N行M列的棋盘上,让你放若干个炮,可以是0个,使得没有一个炮可以攻击另一个炮,请问有多少种放置方法。

题解:

因为每一行每一列的炮的数量<=2

考虑开dp数组存储有几列放了一个炮,有几列放了两个炮

dpi[k],表示放了前i行,有j列是有一个棋子,有k列是有2个棋子的合法方案数

空的序列就是合法的,即M-j-k

 

接下来分类讨论:

一、不放棋子

dpi[k]=dpi-1[k]

二、放一个棋子

放在一个棋子的列:

我们在某一个有一个棋子的列放置棋子,会使这一列变成有两个棋子。

即我们要得到dpi[k]需要在j+1个有一个棋子的列放置棋子,变为有j个有一个棋子的列

而我们有会得到新的有两个棋子的列,因此我们之前必须有k-1个有两个棋子的列。

放在没有棋子的列:

在一个没有棋子的列放置棋子,会得到一个新的有一个棋子的列,即我们要从j-1得到j。

而这时候,我们有两个棋子的列的数量不会变。可以在空列中的任何一列放置这个棋子。

即dpi[k]+=dpi-1[k-1]*(j+1)

dpi[k]+=dpi-1[k]*(M-(j-1)-k)

三、放两个棋子

一个放在有一个棋子的列,一个放在没有棋子的

都放在没有棋子的列

都放在有一个棋子的列

 

#include<bits/stdc++.h>
using namespace std;
const int maxn=114;
const int mod=9999973;
typedef long long ll;
int N,M;
ll ans;
ll dp[maxn][maxn][maxn];
ll cal (int x) {
    return (x*(x-1)/2)%mod;
}
int main () {
    scanf("%d%d",&N,&M);
    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++) {
                dp[i][j][k]=dp[i-1][j][k];
                if (k>=1) dp[i][j][k]+=dp[i-1][j+1][k-1]*(j+1);
                if (j>=1) dp[i][j][k]+=dp[i-1][j-1][k]*(M-j-k+1);
                if (k>=2) dp[i][j][k]+=dp[i-1][j+2][k-2]*(((j+2)*(j+1)/2));
                if (k>=1) dp[i][j][k]+=dp[i-1][j][k-1]*j*(M-j-k+1);
                if (j>=2) dp[i][j][k]+=dp[i-1][j-2][k]*cal(M-j-k+2);
                dp[i][j][k]%=mod; 
            }
        }
    }
    for (int i=0;i<=M;i++) 
        for (int j=0;j<=M;j++)
            ans+=dp[N][i][j],ans%=mod;
    printf("%lld\n",(ans+mod)%mod);
    return 0; 
}

 

posted @ 2020-03-07 11:39  zlc0405  阅读(122)  评论(0编辑  收藏  举报