【题解】BZOJ4806 炮

Description D e s c r i p t i o n

传送门

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

Solution S o l u t i o n

一看就是DP。

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

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

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

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

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

  • 不放炮

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

  • 在没炮的地方放一个

    d(i,j,k)=d(i,j,k)+d(i1,j1,k)(m(j1)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(i1,j+1,k1)(j+1) 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(i1,j2,k)C(m(j2)k,2) 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(i1,j,k1)(mj(k1))j 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(i1,j+2,k2)C(j+2,2) d ( i , j , k ) = d ( i , j , k ) + d ( i − 1 , j + 2 , k − 2 ) ∗ C ( j + 2 , 2 )

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

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

Code C o d e

#include
#include
#include
#include
#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;
}

你可能感兴趣的:(DP,BZOJ)