【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]进行累加统计答案即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }