首页 > 代码库 > 洛谷OJ 2051 中国象棋 DP(计数)

洛谷OJ 2051 中国象棋 DP(计数)

https://www.luogu.org/problem/show?pid=2051

题意:n*m棋盘,n,m<=100 问在棋盘上放炮 使得任意两个炮都互补攻击(即同行或者同列最多放两个)的方法数?
定义状态,dp[i][j][k] 已经放了i行 有j列放一个炮,有k列放2个炮 (并不需要准确知道当前棋盘的状态)
状态转移时,按照第i行如何放炮即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=9999973;
const int N=1e2+20;
int n,m;
ll dp[N][N][N];//dp[i][j][k] 已经放了i行 有j列放一个炮,有k列放2个炮 
ll C(ll x)
{
	return x*(x-1)/2;
}
int main()
{
	while(cin>>n>>m)
	{
		memset(dp,0,sizeof(dp));
		dp[0][0][0]=1;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<=m;j++)
			{
				for(int k=0;k<=m-j;k++)
				{
					dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;//第i+1行不放
					
					//第i+1行 放一个 
					if(m-j-k>=1)
						dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k))%mod;//放在空列上 
					if(j-1>=0)
						dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j)%mod;//放在有一个棋子的列上	
				
					//第i+1行 放两个 
					if(m-j-k>=2)
						dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*C(m-j-k))%mod;//空列 
					if(m-j-k>=1&&j>=1)
						dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)*j)%mod;//一个在空列,一个在有一个棋子的列上 
					if(j-2>=0)
						dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*C(j))%mod;//两个都在有一个棋子的列上 
					
				}
			}
		}
		ll ans=0;
		for(int j=0;j<=m;j++)
			for(int k=0;k<=m-j;k++)
				ans=(ans+dp[n][j][k])%mod;
		cout<<ans<<endl;
	}
	return 0;
} 

  

 

洛谷OJ 2051 中国象棋 DP(计数)