状压DP之中国象棋

题目

传送们
这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

输入格式

一行包含两个整数N,M,之间由一个空格隔开。

输出格式

总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

输入输出样例

样例输入

1 3 

样例输出

7

思路

定义dp[i][j][k]数组代表第i行中,有j列有一个棋子,有k列有两个棋子,我们从当前状态递推下一状态,有6种情况

  1. 不放棋子,(dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod);
  2. 在没有棋子的一列中放一个棋子,(dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k))%mod);
  3. 在没有棋子的两列中放棋子,(dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*c(m-j-k))%mod);(c函数为求(C^2_n)
  4. 在有一个棋子的一列中放棋子,(dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j)%mod);
  5. 在有一个棋子的两列放棋子,(dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*c(j))%mod);
  6. 在有一个棋子的一列和没有棋子的一列放棋子,(dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)*(j))%mod);
    然后跑个二维求解即可

附上代码一份

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int mod=9999973;
ll dp[110][110][110];
inline int c(int x){
	  return x*(x-1)/2;	
}
int main(){
	scanf("%d%d",&n,&m);
	dp[0][0][0]=1;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			for(int k=0;k+j<=m;k++){
				if(dp[i][j][k]){
					dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%mod;
					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(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(j>=1)dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j)%mod;
					if(j>=2)dp[i+1][j-2][k+2]=(dp[i+1][j-2][k+2]+dp[i][j][k]*c(j))%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;
				}
			}
		}
	}
	long long ans=0;
	for(int i=0;i<=m;i++){
		for(int j=0;j+i<=m;j++){
			ans=(ans+dp[n][i][j])%mod;
		}
	}
	printf("%lld",ans);

}
原文地址:https://www.cnblogs.com/soda-ma/p/13229553.html