BZOJ 1801 chess 中国象棋

题意:

在n * m 的棋盘上放"炮",使得任意两个炮都不会互相攻击,求方案数。

 

题解:

既然是求方案数,那么考虑数学方法和递推,这一道题的限制条件比较麻烦,数学方法不合适,考虑递推。

现在考虑定义状态,很显然一维需要行数,表示考虑了前i行,现在在第i行放棋子,需要明白的是每一行每一列最多放两个炮,那么放棋子是有限制条件的,也就是每一列的已经放了的棋子数目在限制当前的放法,那么我们需要再开三维表示,没放棋子的列数,放一个棋子的列数,放两个棋子的列数,仔细一想没放棋子的列数可以通过后两个计算出来,并且第一维也可以滚动起来。

那么转移也变得很显然了。

1.第i行不放棋子。

2.第i行只放一个棋子 : ①放在没放棋子的那一列 ②放在放了一个棋子的那一列

3.第i行放两个棋子: ①全都放在没放棋子的列上 ②分别放在没放棋子的列和放了一个棋子的列上 ③全都放在放了一个棋子的列上

 

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

#define ll long long
const int N = 1e2 + 7;
const int mod = 9999973;
int n, m;
ll dp[N][N][N];

int calc (int x) {
	return x * (x - 1) / 2;
}

int main () {
	scanf ("%d%d", &n, &m);
	dp[0][0][0] = 1;
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			for (int k = 0; k <= m; ++k) {
				dp[i][j][k] += dp[i - 1][j][k];
				if (j >= 1) dp[i][j][k] += dp[i - 1][j - 1][k] * (m - j - k + 1);
				if (j >= 2) dp[i][j][k] += dp[i - 1][j - 2][k] * calc(m - k - j + 2);
				if (k >= 1 && j <= m - 1) dp[i][j][k] += dp[i - 1][j + 1][k - 1] * (j + 1);
				if (k >= 2 && j <= m - 2) dp[i][j][k] += dp[i - 1][j + 2][k - 2] * calc(j + 2);
				if (k >= 1) dp[i][j][k] += dp[i - 1][j][k - 1] * (m - k - j + 1) * j;
				dp[i][j][k] %= mod;
			}
		}
	}
	ll ret = 0;
	for (int j = 0; j <= m; ++j) 
		for (int k = 0; k <= m; ++k) 
			ret = (ret + dp[n][j][k]) % mod;
	cout << ret << endl;
	return 0;
}

  

 

posted @ 2016-10-23 21:26  xgtao  阅读(186)  评论(0编辑  收藏  举报