【BZOJ1801】中国象棋

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1801


很久之前就听说过这道题了,当时就觉得很难。。。

一种很暴力的思想是,用状压DP做,记录每一行的各个状态,但显然,只可以拿50分(也不少)。

这里用到一种DP优化的思想,合并本质相同的状态,实际上,我们并不太关心每一行的摆放情况,真正影响方案数的是,放到某一行,已经放了1个的列数,放了2个的列数及空列,知道了这些就可以进行转移了。

所以设dp[i][j][k]表示放到第i行,放了1个的列有j个,放了2个的列有k个,自然空列就是m-j-k。

然后分类讨论第i行的摆放情况,可以一个也没放,可以放一个在空列或放了1个的列,可以放两个在空列,一个在空列一个在放了一个的列,两个都在放了一个的列。

然后对于dp[n][j][k]进行累加统计答案即可。

 1 #include <cstdio>
 2 
 3 typedef long long ll;
 4 
 5 const int maxn = 105, maxm = 105, p = 9999973;
 6 
 7 ll dp[maxn][maxm][maxm];
 8 
 9 inline int c(int x) {
10     return x * (x - 1) / 2;
11 }
12 
13 int main() {
14     int n, m;
15     ll ans = 0;
16     scanf("%d%d", &n, &m);
17     dp[0][0][0] = 1;
18     for (int i = 1; i <= n; ++i)
19         for (int j = 0; j <= m; ++j)
20             for (int k = 0; k <= m - j; ++k) {
21                 dp[i][j][k] = dp[i - 1][j][k];
22                 if (j >= 1) dp[i][j][k] += dp[i - 1][j - 1][k] * (m - j - k + 1);
23                 if (k >= 1) dp[i][j][k] += dp[i - 1][j + 1][k - 1] * (j + 1);
24                 if (j >= 2) dp[i][j][k] += dp[i - 1][j - 2][k] * c(m - j - k + 2);
25                 if (k >= 1) dp[i][j][k] += dp[i - 1][j][k - 1] * (m - j - k + 1) * j;
26                 if (k >= 2) dp[i][j][k] += dp[i - 1][j + 2][k - 2] * c(j + 2);
27                 dp[i][j][k] %= p;
28                 if (i == n) ans = (ans + dp[i][j][k]) % p;
29             }
30     printf("%lld", ans);
31     return 0;
32 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9890907.html