[AHOI2009]中国象棋 状压dp 状态新颖

看了题后,可以发现一行最多两个炮,一列最多两个炮。
做状压dp一般都是枚举行来做,所以一行两炮可以在一次for中枚举出来,但列上炮的数量不好计算。看了dalao的题解,发现状态这样设置
d p [ i ] [ j ] [ k ] dp[i][j][k] i i 代表枚举的行数, j j 代表有 j j 有1个棋子, k k 表示有 k k 列有两个棋子
于是状态转移有

  1. 不放
  2. 放一个在:无棋子的列上(乘上没有棋子的个数) / 一个棋子的列上(乘上有一个棋子的个数)
  3. 放两个在:都在无棋子的列上(乘上 C n 2 C_n^2 n n 为没有棋子的列数) / 一个在一个棋子列上,一个在无棋子列上(乘上两种列数的个数) / 两个都在一个棋子列上 (乘上 C n 2 C_n^2 n n 为有一个棋子的列数)
    详细看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline void read(int &x){
    x = 0; int f = 1; char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
const int N = 101;
const int MOD = 9999973;
int n, m, ans;
int f[N][N][N];
inline int C(int x){return x * (x - 1) / 2;}
signed main(){
    read(n), read(m);
    f[0][0][0] = 1;
    for (int i = 1; i <= n; i++)
    for (int j = 0; j <= m; j++)
    for (int k = 0; k + j <= m; k++) if (f[i - 1][j][k]){
        (f[i][j][k] += f[i - 1][j][k]) % MOD;//不放
        if (m - j - k >= 1) (f[i][j + 1][k] += f[i - 1][j][k] * (m - j - k)) %= MOD;
        //放一个在无棋子的列上
        if (j >= 1) (f[i][j - 1][k + 1] += f[i - 1][j][k] * j) %= MOD;
        //放一个在有一个棋子的列上
        if (m - j - k >= 2) (f[i][j + 2][k] += f[i - 1][j][k] * C(m - j - k)) %= MOD;
        //放两个都在无棋子的列上
        if (j >= 1 && m - j - k >= 1) (f[i][j][k + 1] += f[i - 1][j][k] * j * (m - j - k)) %= MOD;
        //放两个一个在一个棋子列上,一个在无棋子列上
        if (j >= 2) (f[i][j - 2][k + 2] += f[i - 1][j][k] * C(j)) %= MOD;
        //放两个都在一个棋子列上
    }
    for (int i = 0; i <= m; i++)
    for (int j = 0; i + j <= m; j++)
    (ans += f[n][i][j]) %= MOD;
    //把所有状态累计就是答案
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44627639/article/details/88536128
今日推荐