中國象棋
總時間限制: 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 個 使得沒有任何乙個炮可以攻擊另乙個炮,請問有多少種放置方法。大家肯定很清楚,在中國象棋中炮的行走方式是 乙個炮能攻擊到另乙個炮,當且僅當它們在同一行或同一列中,且它們之間恰好有乙個棋中。你也來和小可可...