思路
因为在同一行或同一列中最多放两个炮,所以可以放0、1、2个炮,三个以上的就不合法,因为可以互相打。
对于状态压缩dp,首先需要假设状态,这题的状态就是0、1、2,(列是指第i行中1~m列)0代表一列中没有炮,1代表一列中有一个炮,2代表一列中有2个炮,从第一行往下遍历,寻找所有的状态。
所以我们定义一个数组dp[i][j][k],第一维表示前i行,第二维表示有j列只有一个炮,第三维表示只有k列有两个炮
那么根据容斥原理可知,没有炮的列数有m-j-k列。
因为一列中最多放两个棋子,且对于每一行,每一列只能放一个,不能说放两个,难道让他们结合吗?这里笔者就想当然的结合了,然后就错了。 所以我们可以讨论以下三种情况(所有情况的第i行都是由第i-1行继承下来):
- 不放棋子 第i行的状态可以由第i-1行状态继承,即$dp[i][j][k]=dp[i - 1][j][k]$。
- 放一个棋子 该棋子放在有一个棋子的列上 在j列中拿出一列+1个棋子成为k列中的一员,即$dp[i][j][k]=dp[i-1][j][k+1]*(j+1)$ 该棋子放在空列上 在空列中拿出一列成为j列的一员,即$dp[i][j][k]=dp[i-1][j-1][k]*(m-j-k+1)$
- 放两个棋子 两个棋子分别放在两个空列上 在空列中拿出两列成为j列的两员,即$dp[i][j][k]=dp[i-1][j-2][k]*C_{m-j-k+2}^2$
- 两个棋子一个在有棋子的列一个在空列中 在j列中拿出一个成为k列的一员,在空列中拿出一个成为j列的一员,即 $dp[i][j][k]=dp[i-1][j][k-1]*j*(m-j-k+1)$
- 两个棋子分别放在两个有棋子的列上 在j列拿出两个成为k列的两员,即 $dp[i][j][k]=dp[i-1][j+2][k-2]*C_{j+2}^2$
- 两个棋子放在没有棋子的一列上 这是错误的,上面说道,对于每一行,只能在一列中放一个,不能放两个,否则就会“结合”了。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| ##include <bits/stdc++.h>
using namespace std;
typedef long long ll; typedef long double ld; typedef pair<int, int> pdd;
##define INF 0x7f7f7f ##define mem(a, b) memset(a , b , sizeof(a)) ##define FOR(i, x, n) for(int i = x;i <= n; i++)
const int mod = 9999973;
ll dp[105][105][105];
int n, m;
void solve() { cin >> 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 - j; k++) { dp[i][j][k] = dp[i - 1][j][k]; if(k >= 1) dp[i][j][k] += dp[i - 1][j + 1][k - 1] * (j + 1); if(j >= 1) dp[i][j][k] += dp[i - 1][j - 1][k] * (m - j + 1 - k); if(j >= 2) dp[i][j][k] += dp[i - 1][j - 2][k] * ((m - j - k + 2) * (m - j - k + 1) / 2); if(k >= 1) dp[i][j][k] += dp[i - 1][j][k - 1] * j * (m - j - k + 1); if(k >= 2) dp[i][j][k] += dp[i - 1][j + 2][k - 2] * ((j + 2) * (j + 1) / 2); dp[i][j][k] %= mod; } } } ll ans = 0; for(int j = 0;j <= m; j++) { for(int k = 0;k <= m - j; k++) { ans = (ans + dp[n][j][k]) % mod; } } cout << ans << endl; }
signed main() { ios_base::sync_with_stdio(false); ##ifdef FZT_ACM_LOCAL freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); signed test_index_for_debug = 1; char acm_local_for_debug = 0; do { if (acm_local_for_debug == '$') exit(0); if (test_index_for_debug > 20) throw runtime_error("Check the stdin!!!"); auto start_clock_for_debug = clock(); solve(); auto end_clock_for_debug = clock(); cout << "Test " << test_index_for_debug << " successful" << endl; cerr << "Test " << test_index_for_debug++ << " Run Time: " << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl; cout << "--------------------------------------------------" << endl; } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug)); ##else solve(); ##endif return 0; }
|
本文作者:jujimeizuo
本文地址: https://blog.jujimeizuo.cn/2020/08/12/luogu-p2051/
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0 协议。转载请注明出处!