1. 程式人生 > >題解:bzoj1801: [Ahoi2009]chess 中國象棋

題解:bzoj1801: [Ahoi2009]chess 中國象棋

bits 題解 display num 兩個 lld ber 組合 i++

Description

在N行M列的棋盤上,放若幹個炮可以是0個,使得沒有任何一個炮可以攻擊另一個炮。 請問有多少種放置方法,中國像棋中炮的行走方式大家應該很清楚吧.

Input

一行包含兩個整數N,M,中間用空格分開.

Output

輸出所有的方案數,由於值比較大,輸出其mod 9999973

Sample Input

1 3

Sample Output

7

HINT

除了在3個格子中都放滿炮的的情況外,其它的都可以.

100%的數據中N,M不超過100
50%的數據中,N,M至少有一個數不超過8
30%的數據中,N,M均不超過6

題解:

這道題狀態轉移方程真的好煩啊.....

考慮從上到下一行一行的放,那麽我們在放第i行時,顯然,每一行或每一列,最多放兩個炮。所以,在放第i行時,所有的合法放法其實是確定的了

放法1:一個都不放

放法2:僅放一個,放在一個炮的沒有列上

放法3:僅放一個,放在有一個炮的列上

放法4:放兩個,均放在一個炮都沒有的列上

放法5:放兩個,均放在有一個炮的列上

放法6:放兩個,一個放在有一個炮的列上,另一個放在有一個炮的列上

實際上到這裏我們可以發現:在放第i列時,我們並不關心炮的具體位置,因為我們求的是每種放法的方案數,所以,我們需要的信息只是每一種類型的列數的數量,這其實是一種對信息的壓縮(一個炮的有多少列,兩個炮的有多少列,一個炮都沒有的有多少列)

所以,用f[i][j][k]表示放到第i行時,有一個炮的有j列,有兩個炮的有k列,則一個炮都沒有的就有m-j-k列

然後考慮每種放法的合法方案數,用計數原理和組合數計算即可

狀態轉移方程如下:

f[i][j][k]=f[i-1][j][k]%MOD;
if(j-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j-1][k]*(m-j-k+1)%MOD)%MOD;
if(k-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j+1][k-1]*(j+1)%MOD)%MOD;
if(j-2>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j-2][k]*C(m-k-j+2)%MOD)%MOD;


if(k-2>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j+2][k-2]*C(j+2)%MOD)%MOD;
if(k-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j][k-1]*j*(m-j-k+1)%MOD)%MOD;

技術分享圖片
#include<bits/stdc++.h>
#define MAXN 101
#define MOD 9999973
using namespace std;
int n,m;
long long ans=0;
long long f[MAXN][MAXN][MAXN];
long long C(int N)
{
    return ((N-1)*N)/2;
}
void DP()
{
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=m-j;k++)
                {
                    f[i][j][k]=f[i-1][j][k]%MOD;
                    if(j-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j-1][k]*(m-j-k+1)%MOD)%MOD;
                    if(k-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j+1][k-1]*(j+1)%MOD)%MOD;
                    if(j-2>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j-2][k]*C(m-k-j+2)%MOD)%MOD;
                    if(k-2>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j+2][k-2]*C(j+2)%MOD)%MOD;
                    if(k-1>=0) f[i][j][k]=(f[i][j][k]%MOD+f[i-1][j][k-1]*j*(m-j-k+1)%MOD)%MOD;
                }
}
int main()
{
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&m);
    f[0][0][0]=1;
    DP();
    for(int i=0;i<=m;i++)
        for(int j=0;j<=m-i;j++)
            ans=(ans%MOD+f[n][i][j]%MOD)%MOD;
    printf("%lld",ans);        
    return 0;
}
View Code

題解:bzoj1801: [Ahoi2009]chess 中國象棋