BZOJ1037 [ZJOI2008]生日聚會Party
Description
今天是hidadz小朋友的生日,她邀請了許多朋友來參加她的生日party。 hidadz帶著朋友們來到花園中,打算
坐成一排玩遊戲。為了遊戲不至於無聊,就座的方案應滿足如下條件:對於任意連續的一段,男孩與女孩的數目之
差不超過k。很快,小朋友便找到了一種方案坐了下來開始遊戲。hidadz的好朋友Susie發現,這樣的就座方案其實
是很多的,所以大家很快就找到了一種,那麽到底有多少種呢?熱愛數學的hidadz和她的朋友們開始思考這個問題
…… 假設參加party的人中共有n個男孩與m個女孩,你是否能解答Susie和hidadz的疑問呢?由於這個數目可能很
多,他們只想知道這個數目除以12345678的余數。
Input
僅包含一行共3個整數,分別為男孩數目n,女孩數目m,常數k。
Output
應包含一行,為題中要求的答案。
Sample Input
1 2 1Sample Output
1HINT
n , m ≤ 150,k ≤ 20。
題解
第一眼看過去認為這是數學題。。。不過不會推結論。。。
從dp的角度入手吧。。。
我們設$f_{i,j,a,b}$為$i$個男孩,$j$個女孩排成一排,滿足題目的條件且任意後綴的男孩數至多比女孩數多$a$個,至多比女孩少$b$個的方案數,那麽顯然,答案為$f_{n,m,k,k}$。
邊界:$f_{0,0,a,b}=1$;
遞推式:兩種選擇,最後一個放男孩和最後一個放女孩。若放男孩,那除他之外末尾的男孩可以比女孩多$a-1$個,少$min(b+1,k)$個,女孩同理。即:
$$f_{i,j,a,b}=f_{i-1,j,a-1,min(b+1,k)}+f_{i,j-1,min(a+1,k),b-1}$$
dp時按$i+j$遞增序(不過好像沒有必要,直接枚舉$i,j$分別遞增就行)。
復雜度$O(nmk^2)$。
附代碼:
#include <algorithm> #include <cstdio> using std::min; using std::max; const int N = 155; const int K = 25; const int mod = 12345678; int f[N][N][K][K]; int main() { int n, m, k; scanf("%d%d%d", &n, &m, &k); for (int i = 0; i <= k; ++i) for (int j = 0; j <= k; ++j) f[0][0][i][j] = 1; for (int ij = 1; ij <= n + m; ++ij) for (int i = max(ij - m, 0); i <= min(ij, n); ++i) { int j = ij - i; for (int a = 0; a <= k; ++a) for (int b = 0; b <= k; ++b) { f[i][j][a][b] = 0; if (i && a) f[i][j][a][b] += f[i - 1][j][a - 1][min(b + 1, k)]; if (j && b) f[i][j][a][b] += f[i][j - 1][min(a + 1, k)][b - 1]; f[i][j][a][b] %= mod; } } printf("%d\n", f[n][m][k][k]); return 0; }
BZOJ1037 [ZJOI2008]生日聚會Party