1. 程式人生 > >[luogu2051][bzoj1801][AHOI2009]chess中國象棋

[luogu2051][bzoj1801][AHOI2009]chess中國象棋

bits 感想 必須 int def 解決 棋盤 pac c++

題目描述

這次小可可想解決的難題和中國象棋有關,在一個N行M列的棋盤上,讓你放若幹個炮(可以是0個),使得沒有一個炮可以攻擊到另一個炮,請問有多少種放置方法。大家肯定很清楚,在中國象棋中炮的行走方式是:一個炮攻擊到另一個炮,當且僅當它們在同一行或同一列中,且它們之間恰好 有一個棋子。你也來和小可可一起鍛煉一下思維吧!

題目大意

在一個n*m的棋盤中,求任意行和任意列上最多有2個棋子的方案總數。

感想

真的是一道DP好題,狀態,轉移方程都非常的難想,我也WA了好幾發。

分析

看到題目,很明顯可以發現的是,因為炮必須是要隔山打虎,那麽任意列和任意行都不能有兩個棋子。(其實我非常想把題目改成3個,這樣難度有加大了)。

因為每一行每一列<=2,所以
定義狀態:
\[f[i][j][k]\]
表示前\(i\)中,有\(j\)列有\(1\)個棋子,有\(k\)列有\(2\)個棋子有多少種方案數。
決策分\(3\)種:


第一種是放\(0\)個棋子
這種狀態很明顯只有轉移,也就是可以從上一層直接推過來,\(j\)\(k\)都不變。
轉移方程:\(f[i][j][k]+=f[i-1][j][k]\)


第二種是放一個\(1\)個棋子:

  • \(1\)顆棋子放在沒有棋子的列上,那麽這樣會使現在多\(1\)列有\(1\)個棋子的列,那麽原來比現在要少\(1\),也就是可以從\(f[i-1][j-1][k]\)轉移過來,但是在原來的狀態上,有\(m-(j-1)-k\)
    列是沒有棋子的,也就是這幾列上都是可以放棋子,根據乘法原理得到轉移方程就是:\(f[i][j][k]+=f[i-1][j-1][k]*(m-(j-1)-k)\)
  • \(1\)顆棋子放在有1個棋子的列上,那麽就會讓減少原來的有\(1\)個棋子的列,增加一個\(2\)個棋子的列,也就是說原來有\(j+1\)列1個棋子的列,有\(k-1\)個2個棋子的列,得到轉移方程就是\(f[i][j][k]+=f[i-1][j+1][k-1]*(j+1)\)

第三種是放一個\(2\)個棋子:

  • \(1\)顆棋子放在沒有棋子的列上,\(1\)顆棋子放在有\(1\)個棋子的列上,那麽很明顯這樣原來的狀態就是\(f[i-1][j][k-1]\)
    ,因為有一列從\(0\)變成了\(1\),有一列從\(1\)變成了\(2\),就相當於是一列從0變成了2,那麽轉移方程就是:\(f[i][j][k]+=f[i-1][j][k-1]*(m-j-(k-1))\)
  • \(2\)顆棋子都放在沒有棋子的列上,而且兩個棋子在不同的列上,那麽原來就有\(j-2\)列是只有\(1\)個棋子,可得原來的狀態就是\(f[i-1][j-2][k]\),因為不能有在同一列上,那麽需要對剩余沒有棋子的列進行排列組合,剩下來的點數為\(m-(j-2)-k\),取兩個那麽就是\(C^{2}_{m-(j-2)-k}\),得到轉移方程:\(f[i][j][k]+=f[i-1][j-2][k]*C^{2}_{m-(j-2)-k}\)
  • \(2\)顆棋子都放在有一個棋子的列上,而且兩個棋子不在同一列,那麽現在減少了\(2\)個有\(1\)個棋子的列,增加了\(2\)個有\(2\)個棋子的列,推出原來的狀態是\(f[i-1][j+2][k-2]\),在此根據排列組合,乘法原理得到轉移方程是:\(f[i-1][j+2][k-2]*C^{2}_{j+2}\)

綜上,我們整理一下轉移方程:
\[if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1))\]
\[if (j >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - (j - 1) - k))\]
\[if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - (k - 1)))\]
\[if (j >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * calc(m - (j - 2) - k))\]
\[if (k >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * calc(j + 2))\]
其中calc就是求組合數。

ac代碼

#include <bits/stdc++.h>
#define ll long long
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
#define mod 9999973
#define N 105
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
ll f[N][N][N];
int n, m;
ll calc(ll x) {
    return (x * (x - 1)) / 2 % mod;
}
int main() {
    read(n); read(m);
    f[0][0][0] = 1ll;
    for (int i = 1; i <= n; i ++) {
        for (int j = 0; j <= m; j ++) {
            for (int k = 0; k <= m - j; k ++) {
                f[i][j][k] = f[i - 1][j][k];
                if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1)) % mod;
                if (j >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - (j - 1) - k)) % mod;
                if (k >= 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - (k - 1))) % mod;
                if (j >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * calc(m - (j - 2) - k)) % mod;
                if (k >= 2) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * calc(j + 2)) % mod;
                f[i][j][k] %= mod;
            }
        }
    }
    ll ans = 0;
    for (int i = 0; i <= m; i ++) {
        for (int j = 0; j <= m ; j ++) {
            ans = (ans + f[n][i][j]) % mod;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

[luogu2051][bzoj1801][AHOI2009]chess中國象棋