【题解】BZOJ4806 炮

D e s c r i p t i o n

传送门

有一个 n m 的一个棋盘,问在棋盘上面摆放炮且炮与炮之间互不攻击的方案数。

S o l u t i o n

一看就是DP。

看到 n , m 相对于状压DP的数据规模来说比较大,因此不好状压。

仔细分析,炮与炮之间发生冲突当且仅当所在的行或列上有 3 个及以上的炮。

而且,只要一列上的炮数少于 3 个,无论这两列摆在什么位置,这几个炮都不会相互攻击。由此,我们并不需要记录这几个炮的具体摆放位置,只要知道放有 1 个或 2 个炮的位置就行了,这样没有摆炮的列数也可以推出来。

因此,我们用 d ( i , j , k ) 表示前 i 行有 j 列摆放了 1 个炮,有 k 列摆放了 2 个炮。

在第 i 行最多能放两个炮,因此分情况考虑写状态转移方程(状态转移计算均在取模的意义下进行)

  • 不放炮

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j , k )

  • 在没炮的地方放一个

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j 1 , k ) ( m ( j 1 ) k )

  • 在有一个炮的地方放一个

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j + 1 , k 1 ) ( j + 1 )

  • 在两个没炮的地方各放一个

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j 2 , k ) C ( m ( j 2 ) k , 2 )

  • 在没炮的地方和一个炮的地方各放一个

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j , k 1 ) ( m j ( k 1 ) ) j

  • 在两个有一个炮的地方各放一个

    d ( i , j , k ) = d ( i , j , k ) + d ( i 1 , j + 2 , k 2 ) C ( j + 2 , 2 )

状态转移方程有点多,需要细心。

然后看一看这个状态转移方程,发现它只于之前的状态有关,我们就可以用滚动数组来优化空间。

C o d e

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 105
#define p 999983
#define calc(x) ((x) * ((x) - 1) / 2 % p)
long long d[2][MAXN][MAXN];
int main() {
    int n, m, cur = 0;
    scanf("%d%d", &n, &m);
    d[0][0][0] = 1;
    for (int i = 1; i <= n; i++) {
        cur ^= 1;
        for (int j = 0; j <= m; j++)
            for (int k = 0; k + j <= m; k++) {
                d[cur][j][k] = d[cur ^ 1][j][k];
                if (j >= 1) d[cur][j][k] += d[cur ^ 1][j - 1][k] * (m - (j - 1) - k);
                if (k >= 1) d[cur][j][k] += d[cur ^ 1][j + 1][k - 1] * (j + 1);
                if (j >= 2) d[cur][j][k] += d[cur ^ 1][j - 2][k] * calc(m - (j - 2) - k);
                if (k >= 1) d[cur][j][k] += d[cur ^ 1][j][k - 1] * (m - j - (k - 1)) * j;
                if (k >= 2) d[cur][j][k] += d[cur ^ 1][j + 2][k - 2] * calc(j + 2);
                d[cur][j][k] %= p;
            }
    }
    long long ans = 0;
    for (int i = 0; i <= m; i++)
        for (int j = 0; i + j <= m; j++) 
            (ans += d[cur][i][j]) %= p;
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Diogenes_/article/details/81025256