[BZOJ 1801] [Ahoi2009]chess 中国象棋 【DP】

题目链接:BZOJ - 1801

题目分析

对于50%的数据是可以直接状压 DP 的。

对于100%的数据,使用递推的 DP 。(或者这只叫递推不叫 DP ?)

可以发现,每一行和每一列的棋子个数不能超过 2 个。

用 f[i][j][k] 表示前 i 行,有 j 列有 1 个棋子,有 k 列有 2 个棋子的方案数。(有 0 个棋子的列数可以用 m - j - k 得到。)

初始状态 f[0][0][0] = 1;

转移时从第 i - 1 行转移过来,由于一行最多放 2 个棋子,所以可以讨论各种情况,转移一下。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int MaxN = 100 + 5;

typedef long long LL;

const LL Mod = 9999973;

int n, m;

LL Ans;
LL f[MaxN][MaxN][MaxN];

LL C2(int a) {
	LL ret;
	ret = (LL)a * (LL)(a - 1) / 2ll % Mod;
	return ret;
}

int main() 
{
	scanf("%d%d", &n, &m);
	f[0][0][0] = 1;
	Ans = 0ll;
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j <= m; ++j) {
			for (int k = 0; k <= m - j; ++k) {
				f[i][j][k] = f[i - 1][j][k];
				if (j >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j - 1][k] * (LL)(m - (j - 1) - k) % Mod)) % Mod;	
				if (k >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j + 1][k - 1] * (LL)(j + 1) % Mod)) % Mod;
				if (j >= 2) f[i][j][k] = (f[i][j][k] + (f[i - 1][j - 2][k] * C2(m - (j - 2) - k) % Mod)) % Mod;
				if (k >= 2) f[i][j][k] = (f[i][j][k] + (f[i - 1][j + 2][k - 2] * C2(j + 2) % Mod)) % Mod;
				if (j >= 1 && k >= 1) f[i][j][k] = (f[i][j][k] + (f[i - 1][j][k - 1]) * (LL)(j) * (LL)(m - j - (k - 1)) % Mod) % Mod;
				if (i == n) Ans = (Ans + f[i][j][k]) % Mod;
			}
		}
	}
	printf("%lld
", Ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/JoeFan/p/4261521.html