BZOJ1801: [Ahoi2009]chess 中国象棋

BZOJ1801: [Ahoi2009]chess 中国象棋

Description

在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.

Input

一行包含两个整数N,M,中间用空格分开.

Output

输出所有的方案数,由于值比较大,输出其mod 9999973

Sample Input

1 3

Sample Output

7

HINT

除了在3个格子中都放满炮的的情况外,其它的都可以.

100%的数据中N,M不超过100
50%的数据中,N,M至少有一个数不超过8
30%的数据中,N,M均不超过6


题解Here!

一眼看去——棋盘$DP$。
首先,每行、每列最多放俩炮,这个不用多说。
对于$50%$的数据,直接状压$DP$对吧。
然后我们发现,我们对于棋子的顺序与位置并不关心。
也就是说,并不需要知道准确的状态。
那么我们就可以把状态压缩省去了,换成讲的状态记录。
设$dp[i][j][k]$表示当前正在填第$i+1$行,前$i$行中有$j$列放了一个棋子,有$k$列放了两个棋子。
然后转移就是分类讨论,运用加法原理和乘法原理即可,具体可以参见代码。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 110
#define MOD 9999973
using namespace std;
int n,m;
long long ans=0,dp[MAXN][MAXN][MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline int C(int x){return x*(x-1)/2;}
void solve(){
	dp[0][0][0]=1;
	for(int i=0;i<n;i++)
	for(int j=0;j<=m;j++)
	for(int k=0;j+k<=m;k++)
	if(dp[i][j][k]){
		dp[i+1][j][k]=(dp[i+1][j][k]+dp[i][j][k])%MOD;//一个棋子都不放
		if(m-k-j>=1)//放一个,在没有棋子的那一列
			dp[i+1][j+1][k]=(dp[i+1][j+1][k]+dp[i][j][k]*(m-j-k)%MOD)%MOD;
		if(j>=1)//放一个,在有一个棋子的那一列
			dp[i+1][j-1][k+1]=(dp[i+1][j-1][k+1]+dp[i][j][k]*j%MOD)%MOD;
		if(m-k-j>=1&&j>=1)//放两个,一个在没有棋子的列,一个在有一个棋子的列
			dp[i+1][j][k+1]=(dp[i+1][j][k+1]+dp[i][j][k]*(m-j-k)%MOD*j%MOD)%MOD;
		if(m-k-j>=2)//放两个,都在没有棋子的两列
			dp[i+1][j+2][k]=(dp[i+1][j+2][k]+dp[i][j][k]*C(m-j-k)%MOD)%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)%MOD;
	}
}
void work(){
	n=read();m=read();
	solve();
	for(int i=0;i<=m;i++)
	for(int j=0;i+j<=m;j++)
	ans=(ans+dp[n][i][j])%MOD;//统计方案数
	printf("%lld\n",ans);
}
int main(){
	work();
    return 0;
}

  

posted @ 2018-10-27 16:34  符拉迪沃斯托克  阅读(225)  评论(0编辑  收藏  举报
Live2D