[AHOI2009]中國象棋
阿新 • • 發佈:2019-02-06
題目描述
這次小可可想解決的難題和中國象棋有關,在一個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; }