【bzoj1801】[Ahoi2009]chess 中国象棋 dp

题目描述

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

输入

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

输出

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

样例输入

1 3

样例输出

7


题解

dp

首先必须知道,每行最多有两个炮,每列也最多有两个炮。

那么我们只需要记录某棋盘有一个炮的列数和有两个炮的列数,然后讨论一行放炮的个数及位置即可。

设f[i][j][k]表示前i行,有j列有一个炮,有k列有两个炮的方案数。

那么对于每行有以下6种情况:

1.不放炮

2.放一个炮在没有炮的列上

3.放一个炮在有一个炮的列上

4.放两个炮在没有炮的列上

5.放一个炮在没有炮的列上,放一个炮在有一个炮的列上

6.放两个炮在有一个炮的列上

用乘法原理和排列组合求出每种情况可选择的方案数,乘上原方案数,累加到新方案数上。

注意一下边界什么的就好了。

还有这道题需要开long long,否则极限情况乘法会爆int。

#include <cstdio>
#define MOD 9999973
typedef long long ll;
ll f[110][110][110];
void calc(ll &a , const ll b)
{
	a = (a + b) % MOD;
}
int main()
{
	int n , m , i , j , k;
	ll ans = 0;
	scanf("%d%d" , &n , &m);
	f[0][0][0] = 1;
	for(i = 1 ; i <= n ; i ++ )
	{
		for(j = 0 ; j <= m ; j ++ )
		{
			for(k = 0 ; k <= m ; k ++ )
			{
				calc(f[i][j][k] , f[i - 1][j][k]);
				if(j) calc(f[i][j][k] , f[i - 1][j - 1][k] * (m - j + 1 - k));
				if(k) calc(f[i][j][k] , f[i - 1][j + 1][k - 1] * (j + 1));
				if(j >= 2) calc(f[i][j][k] , f[i - 1][j - 2][k] * (m - j + 2 - k) * (m - j + 1 - k) / 2);
				if(j && k) calc(f[i][j][k] , f[i - 1][j][k - 1] * j * (m - j - k + 1));
				if(k >= 2) calc(f[i][j][k] , f[i - 1][j + 2][k - 2] * (j + 2) * (j + 1) / 2);
			}
		}
	}
	for(i = 0 ; i <= m ; i ++ )
		for(j = 0 ; j <= m ; j ++ )
			calc(ans , f[n][i][j]);
	printf("%lld\n" , ans);
	return 0;
}

 

posted @ 2017-03-24 18:42  GXZlegend  阅读(299)  评论(0编辑  收藏  举报