[P2051 [AHOI2009]中国象棋] DP
https://www.luogu.org/problemnew/show/P2051
题目描述
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!
输入输出格式
输入格式:
一行包含两个整数N,M,之间由一个空格隔开。
输出格式:
总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。
输入输出样例
说明
样例说明
除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。
数据范围
100%的数据中N和M均不超过100
50%的数据中N和M至少有一个数不超过8
30%的数据中N和M均不超过6
题解:每行每列只能放置0~2个棋子,所以可以按行顺序DP,每行放置0~2个保证行合法(转移过程限制这个条件),用dp[i][j][k]表示前i行有j列有1个棋子,有k列有两个棋子,保证列合法(状态数组dp限制了这个条件)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long ll; 7 const ll mod=9999973; 8 ll dp[105][105][105]; 9 ll C(ll x){ 10 return x*(x-1)/2%mod; 11 } 12 int main(){ 13 int n,m; 14 scanf("%d%d",&n,&m); 15 dp[0][0][0]=1; 16 ll ans=0; 17 for(int i=1;i<=n;i++){ 18 for(int j=0;j<=m;j++){ 19 for(int k=0;k<=m;k++){ 20 dp[i][j][k]+=dp[i-1][j][k]%mod; 21 dp[i][j][k]%=mod; 22 if(k)dp[i][j][k]+=(dp[i-1][j+1][k-1]*(j+1))%mod; 23 dp[i][j][k]%=mod; 24 if(j)dp[i][j][k]+=dp[i-1][j-1][k]*(m-j+1-k)%mod; 25 dp[i][j][k]%=mod; 26 if(j>=2)dp[i][j][k]+=dp[i-1][j-2][k]*C(m-j+2-k)%mod; 27 dp[i][j][k]%=mod; 28 if(k>=2)dp[i][j][k]+=(dp[i-1][j+2][k-2]*C(j+2))%mod; 29 dp[i][j][k]%=mod; 30 if(k)dp[i][j][k]+=dp[i-1][j][k-1]*(j*(m-j+1-k))%mod; 31 dp[i][j][k]%=mod; 32 if(i==n)ans+=dp[i][j][k]; 33 ans%=mod; 34 } 35 } 36 37 } 38 cout<<ans <<endl; 39 return 0; 40 }