洛谷 P2051 [AHOI2009]中國象棋
阿新 • • 發佈:2018-12-10
這道題主要是狀態很難想到 首先可以看出每行每列不能超過2個棋子 也就是說有0, 1, 2三種狀態
所以可以一行一行來處理 那就用表示前i行,有列放了一個棋子,有列放了2個棋子的方案數 放了0個棋子的列數是 那麼這個時候狀態轉移方程就非常好寫了。 對於當前這一行可以不放,放一個,放兩個棋子 0表示沒有棋子的列,1表示有1個棋子的列。 那麼有幾種情況
不放 放一個在0 放一個在1 放兩個都在0 放兩個一個0一個1 放兩個在1
放兩個一個0一個1為例 方程為 這裡用刷表法,比填表法方便非常多,但是要注意有些狀態是不存在的(比如與都等於) 所以一開始要判斷之前有沒有刷到過這個狀態
現在解釋一個這個方程 現在是第i行,要填i+1行 放一個在0的話,就有一個0變成1 所以j要加1 放一個在1的話,就有一個1變成2 所以j要減1,k要加1 這裡0的列數不用管,因為推出j和k就可以知道0的列數了(m-j-k) 所以j加1又減1,所以不變,而k+1 所以是
最後就是把最後一行的所有情況加起來就是答案 注意可以用define來簡化程式碼
#include<cstdio> #include<algorithm> #define REP(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) #define cal(a, b) a = (a + b) % MOD using namespace std; const int MOD = 9999973; const int MAXN = 112; long long f[MAXN][MAXN][MAXN], n, m, ans; int main() { scanf("%d%d", &n, &m); f[0][0][0] = 1; REP(i, 0, n) _for(j, 0, m) for(int k = 0; k + j <= m; k++) { if(!f[i][j][k]) continue; int r = m - j - k; cal(f[i+1][j][k], f[i][j][k]); if(r >= 1) cal(f[i+1][j+1][k], f[i][j][k] * r); if(j >= 1) cal(f[i+1][j-1][k+1], f[i][j][k] * j); if(r >= 2) cal(f[i+1][j+2][k], f[i][j][k] * (r * (r - 1) / 2)); if(r >= 1 && j >= 1) cal(f[i+1][j][k+1], f[i][j][k] * r * j); if(j >= 2) cal(f[i+1][j-2][k+2], f[i][j][k] * (j * (j - 1) / 2)); } _for(j, 0, m) for(int k = 0; k + j <= m; k++) cal(ans, f[n][j][k]); printf("%lld\n", ans); return 0; }