洛谷P2051 中国象棋

题目链接:https://www.luogu.com.cn/problem/P2051

题意:nxm棋盘放棋子,每行每列都不能放超过2个棋子,求方案数

为什么这题有状压dp的标签......我还一直在想状压怎么写,然而这个n,m<=100。

正解是比较巧妙的dp。设f[i][j][k]表示到了第i行,有j列放了1个棋子,k列放了2个棋子的方案数,那么有转移方程:

f[i][j][k]+=f[i-1][j][k],第i行不放棋子

f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1),在一列没放棋子的放1个

f[i][j][k]+=f[i-1][j+1][k-1]*(j+1),在一列放了1个棋子的放1个,使得这列放了2个棋子

f[i][j][k]+=f[i-1][j-2][k]*C(m-k-j+2,2),在两列没放棋子的各放1个,使得这两列都放了1个

f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2,2),在两列放1个棋子的再各放1个,使得这两列放了2个

f[i][j][k]+=f[i-1][j][k-1]*j*(m-k-j+1),在一列放0个的和一列放1个的,各放一个棋子,使得一列放1个,另外一列放2个

注意每个转移方程的边界条件即可

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const ll mod=9999973;
ll f[110][110][110],n,m,i,j,k;

int main(){
	cin>>n>>m;
	memset(f,0,sizeof(f));
	f[0][0][0]=1;
	for (i=1;i<=n;i++)
	  for (j=0;j<=m;j++)
	    for (k=0;k<=m;k++)
	      if (j+k<=m&&j+k*2<=i*2){
	      	f[i][j][k]=f[i-1][j][k];
	      	if (j>=1) f[i][j][k]+=f[i-1][j-1][k]*(m-k-j+1);
	      	if (k>=1) f[i][j][k]+=f[i-1][j+1][k-1]*(j+1);
	      	if (j>=2) f[i][j][k]+=f[i-1][j-2][k]*(m-k-j+2)*(m-k-j+1)/2;
	      	if (k>=1) f[i][j][k]+=f[i-1][j][k-1]*(m-j-k+1)*j;
	      	if (k>=2) f[i][j][k]+=f[i-1][j+2][k-2]*(j+2)*(j+1)/2;
	      	f[i][j][k]%=mod;
		  }
	ll ans=0;
	for (i=0;i<=m;i++)
	  for (j=0;j<=m;j++)
	    if (i+j<=m&&i+j*2<=2*n) ans=(ans+f[n][i][j])%mod;
	cout<<ans<<endl;
	return 0;
}

  

 

posted @ 2020-09-18 16:19  coastal_taipan  阅读(130)  评论(0编辑  收藏  举报