1. 程式人生 > >[AHOI2009]中國象棋

[AHOI2009]中國象棋

題目描述

這次小可可想解決的難題和中國象棋有關,在一個N行M列的棋盤上,讓你放若干個炮(可以是0個),使得沒有一個炮可以攻擊到另一個炮,請問有多少种放置方法。大家肯定很清楚,在中國象棋中炮的行走方式是:一個炮攻擊到另一個炮,當且僅當它們在同一行或同一列中,且它們之間恰好 有一個棋子。你也來和小可可一起鍛鍊一下思維吧!

輸入輸出格式

輸入格式:

一行包含兩個整數N,M,之間由一個空格隔開。

輸出格式:

總共的方案數,由於該值可能很大,只需給出方案數模9999973的結果。

輸入輸出樣例

輸入樣例#1: 複製
1 3
輸出樣例#1: 複製
7

說明

樣例說明

除了3個格子裡都塞滿了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7種方案。

資料範圍

100%的資料中N和M均不超過100

50%的資料中N和M至少有一個數不超過8

30%的資料中N和M均不超過6

題解:

DP

f[i][j][k]表示前i行有j列放了1個炮,k列放了2個炮

轉移:

不放炮f[i-1][j][k]

在1個空列放炮f[i-1][j-1][k]*(m-k-j+1)

在一個有一個炮的列放炮f[i-1][j+1][k-1]*(j+1)

在兩個空列分別放炮f[i-1][j-2][k]*(m-j-k+2)*(m-j-k+1)/2

在一個空列一個有炮的列放炮f[i-1][j][k-1]*(m-j-k+1)*j

在兩個有炮的列f[i-1][j+2][k-2]*(j+2)*(j+1)/2;

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=110;
long long f[maxn][maxn][maxn];
const int mod=9999973;
inline long long mo(long long x){
    return (x%mod+mod)%mod;
}
int main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n,m;
    scanf("%d %d",&n,&m);
    f[0][0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=m;k++){
                if(j+k>m)
                    break;
                f[i][j][k]=mo(f[i-1][j][k]+f[i][j][k]);
                if(j)
                    f[i][j][k]=mo(f[i][j][k]+f[i-1][j-1][k]*(m-k-j+1));
                if(j<m&&k)
                    f[i][j][k]=mo(f[i][j][k]+f[i-1][j+1][k-1]*(j+1));
                if(j>=2)
                    f[i][j][k]=mo(f[i][j][k]+f[i-1][j-2][k]*((m-k-j+2)*(m-k-j+1)/2));
                if(k)
                    f[i][j][k]=mo(f[i][j][k]+f[i-1][j][k-1]*(m-k-j+1)*j);
                if(j+2<=m&&k>=2)
                    f[i][j][k]=mo(f[i][j][k]+f[i-1][j+2][k-2]*((j+2)*(j+1)/2));
            }
    long long ans=0;
    for(int i=0;i<=m;i++)
        for(int j=0;j<=m;j++)
            ans=mo(ans+f[n][i][j]);
    printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}