動態規劃 遞推 中國象棋

2021-08-02 12:47:50 字數 3057 閱讀 5419

中國象棋

總時間限制: 10000ms 單個測試點時間限制: 1000ms 記憶體限制: 65536kb

描述 在n行m列的棋盤上,放若干個炮可以是0個,使得沒有任何乙個炮可以攻擊另乙個炮。請問有多少種放置方法?中國象棋中炮的行走方式大家應該很清楚吧。

輸入 一行包含兩個整數n,m,中間用空格分開。

輸出 輸出所有的方案數,由於值比較大,輸出其mod 9999973

樣例輸入

1 3樣例輸出

7提示

除了在3個格仔中都放滿炮的的情況外,其它的都可以.

100%的資料中n,m不超過100

50%的資料中,n,m至少有乙個數不超過8

30%的資料中,n,m均不超過6

** ahoi2009

這道題拿到手上,看了下資料規模,就已經知道了這道題要用動規。但是怎麼動規呢?就讓我們一步一步地推導吧。

動規的一大難點是定義狀態,如果狀態定義錯了,更不要想找到什麼狀態轉移方程了。好的狀態不僅能讓整個程式得出結果快,而且還好理解。然而,定義狀態的基本前提還是 定義出來的狀態能準確說明一種情況並且滿足動規的兩個原則。

對於這道題而言,首先我們肯定會想到把「已經下了好多棋」定義為乙個狀態。這個方向是正確的,但是不能盲目。首先,我們可以定義「在前i行中下棋」,這樣就可以根據「在前i-1行中下棋」的結果來計算「在前i行中下棋」的結果了。至關重要的是,該怎麼算。我們已經知道,每一行,每一列最多能下兩枚棋,而在第i行的某一列能否下棋,其實是要取決於之前的每列下了多少枚棋的。因此,我們可以考慮這個狀態:

long long f[i][j][k][l]; //下了i行棋,其中有j列上有1枚棋,有k列上有2枚棋,有l列上有0枚棋
看我1 2 0的順序,你也應該知道了:狀態l是可以被砍掉的。因為如果知道了有j列上下了一枚棋,有k列上下了兩枚棋,那麼沒有下棋的列是可以算出來的,為m-j-k。這滿足了「準確說明一種情況」的原則。因此新的狀態為:

long ong f[i][j][k]; //下了i行棋,其中有j列上有1枚棋,有k列上有2枚棋
所以就可以考慮狀態轉移方程了。在根據放前i-1行的方案總數計算前i行的方案總數時在分情況時應遵循加法原理。

long long &cnt = f[i][j][k];

//當前行乙個都不放

cnt=f[i-1][j][k] % mod;

//當前行放乙個

cnt=(cnt + f[i-1][j-1][k] * (m-k-(j-1))) % mod;

cnt=(cnt + f[i-1][j+1][k-1] * (j+1)) % mod;

//當前行放兩個

cnt=(cnt + f[i-1][j-2][k] * c(2, m-k-(j-2))) % mod;

cnt=(cnt + f[i-1][j-1+1][k-1] * (m-(k-1)-j) * j) % mod;

cnt=(cnt + f[i-1][j+2][k-2] * c(2, j+2)) % mod;

可以分6種情況:

1.若第i行乙個都不放,就是這麼多(見**)

2.若第i行放1個

i.若都放在了之前乙個都沒有放的列上,則要用到f[i-1][j-1][k],因為放之前有j-1列上有1個棋子。而能放的地方有m-k-(j-1)個(即放這乙個之前乙個都沒有放的列數),所以根據乘法原理乘上乙個m-k-(j-1)

如果以上推導的思路懂了,後面的就能自己推導了。

ii.放在了之前已經放了乙個的列上。這樣會使放了兩個的列的列數加1,所以要用到f[i-1][j+1][k-1],有j+1個可用位置。

3.若第i行放2個

i.都放在之前乙個都沒有放的列上。乘上的數為c(2, m-k-(j-2))

ii.乙個放在之前沒有放的列上,乙個放在之前放了乙個的列上。乘上的數為(m-(k-1)-j) * (j-1+1)。為什麼要-1+1?因為乙個放在沒有放的列上會使放乙個的列數+1,而另乙個放在放乙個的列上會使放乙個的列數-1,相當於放乙個的列數沒有變。

iii.兩個都放在之前放了乙個的列上。乘上的數為c(2, j+2)

參考**

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

const

int maxn=105;

const

int mod=9999973;

int n,m;

//i,j,k

//放置了前i行,其中j列放置了乙個炮,k列放置了兩個炮

//沒有放置炮的列數為m-i-j

//初始狀態f[0][0][0]=1

long

long f[maxn][maxn][maxn];

long

long c(int up, int down) //未處理溢位,因為這裡的up只可能出現2。這個函式只是為了方便理解 。

for(int i=2; i<=down-maxi; i++)

return ans%mod;

}int main()}}

//最後這點根據加法原理,應該不用多講

long

long ans=0;

for(int j=0; j<=m; j++)}}

printf("%lld\n",ans);

return

0;}

中國象棋2

using system using system.collections.generic using system.linq using system.text using system.drawing namespace chinesechess public override bool mov...

中國象棋 題解

中國象棋這道題才看到的時候,畏難情緒很重啊,先介紹題目,大意是在n行 m列的棋盤上,放若干個炮可以是 0個,使得沒有任何乙個炮可以攻擊另乙個炮。請問有多少種放置方法。考試的時候沒有其他的想法,就只想暴力騙分,用乙個一維的標記陣列,再用乙個遞迴,每排最多放兩個。後面想來,完全可以把每排最多放兩個的情況...

DP 中國象棋

這次小可可想解決的難題和中國象棋有關。在乙個 n 行 m 列的棋盤上,讓你放若干個炮 可以是 0 個 使得沒有任何乙個炮可以攻擊另乙個炮,請問有多少種放置方法。大家肯定很清楚,在中國象棋中炮的行走方式是 乙個炮能攻擊到另乙個炮,當且僅當它們在同一行或同一列中,且它們之間恰好有乙個棋中。你也來和小可可...