洛谷 P2051 [AHOI2009]中国象棋(多维dp)

传送门


解题思路

设dp[i][j][k]为到前i行,有j列放了1个炮,k列放了两个炮的方案数。
显然讨论各种情况,从i-1进行转移即可。
可以滚动数组,但没必要。

  • 一个不放:方案数等于dp[i-1][j][k]。
  • 放一个,放在原来一个没有的列上:方案数等于dp[i-1][j-1][k]*(m-(j-1)-k)
  • 放一个,放在原来有一个炮的列上:方案数等于dp[i-1][j+1][k-1]*(j+1)
  • 放两个,全放在原来一个没有的列上:方案数等于dp[i-1][j-2][k]*(m-(j-2)-k)*(m-(j-2)-k-1)/2
  • 放连个,全放在原来有一个炮的列上:方案数等于dp[i-1][j+2][k-2]*(j+2)*(j+1)/2
  • 放两个,一个放在原来没有炮的列上,一个放在原来有一个炮的列上:方案数等于dp[i-1][j][k-1]*(m-j-(k-1))*j

注意取模和边界判断即可。

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int mod=9999973; 
const int maxn=105;
int n,m;
long long dp[maxn][maxn][maxn];
long long ans;
inline long long c(int x){
	return (long long)x*(x-1)/2;
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=0;i<=n;i++) dp[i][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;
				dp[i][j][k]=dp[i-1][j][k];
				if(j>=1) dp[i][j][k]+=dp[i-1][j-1][k]*(m-j+1-k);
				if(k>=1) dp[i][j][k]+=dp[i-1][j+1][k-1]*(j+1);
				if(j>=2) dp[i][j][k]+=dp[i-1][j-2][k]*c(m-j+2-k);
				if(k>=2) dp[i][j][k]+=dp[i-1][j+2][k-2]*c(j+2);
				if(j>=1&&j-1+k<m) dp[i][j][k]+=dp[i-1][j][k-1]*(m-j+1-k)*j;
				dp[i][j][k]%=mod;
				if(i==n) ans=(ans+dp[i][j][k])%mod;
			}
		}
	}
	cout<<ans;
    return 0;
}
posted @ 2021-09-10 20:56  尹昱钦  阅读(42)  评论(0编辑  收藏  举报