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; }