1. 程式人生 > >[BZOJ 1037][ZJOI2008]生日聚會Party

[BZOJ 1037][ZJOI2008]生日聚會Party

print 今天 cnblogs gre val lan log spl 由於

1037: [ZJOI2008]生日聚會Party

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 2560 Solved: 1538
[Submit][Status][Discuss]

Description

  今天是hidadz小朋友的生日,她邀請了許多朋友來參加她的生日party。 hidadz帶著朋友們來到花園中,打算
坐成一排玩遊戲。為了遊戲不至於無聊,就座的方案應滿足如下條件:對於任意連續的一段,男孩與女孩的數目之
差不超過k。很快,小朋友便找到了一種方案坐了下來開始遊戲。hidadz的好朋友Susie發現,這樣的就座方案其實
是很多的,所以大家很快就找到了一種,那麽到底有多少種呢?熱愛數學的hidadz和她的朋友們開始思考這個問題

…… 假設參加party的人中共有n個男孩與m個女孩,你是否能解答Susie和hidadz的疑問呢?由於這個數目可能很
多,他們只想知道這個數目除以12345678的余數。

Input

  僅包含一行共3個整數,分別為男孩數目n,女孩數目m,常數k。

Output

  應包含一行,為題中要求的答案。

Sample Input

1 2 1

Sample Output

1

HINT

n , m ≤ 150,k ≤ 20。

求方案數量果斷DP一發(霧

(達哥給的題果然賽艇...)

$dg:$ 求方案數的DP的狀態定義就相當於一個限制, 狀態值為符合這個定義的合法方案的數量, 最後求個和就完事了233

據此我們可以定義 $dp[i][j][b][g]$ 為當序列中有 $i$ 個男孩 $j$ 個女孩且後綴序列中男生最多比女生多 $b$ 個, 女生最多比男生多 $g$ 個.

問題來了, 為啥要用後綴?

我們註意到如果我們從前往後遞推的話, 只需保證遞推中的所有後綴符合題目中 $k$ 的限制條件即可. 然後我們為了方便轉移將男女個數差的最大值也定義進狀態. 這樣的話我們只管在狀態後面添人就好了OwO添一個男生轉移一下, 再添個女生轉移一下OwO

然後由於我們存儲的是後綴, 所以我們在後面加人的時候顯然可以讓前面的所有後綴後面都添加一個相應的人, 而差值也會隨之變化. 加男生後必定會 $b+1$ , 可能會 $g-1$. 加女生之後必定會 $g+1$ , 可能會 $b-1$ . 為啥要說"可能會"呢? 因為可能會有男/女生過多結果導致後綴中女/男生無法比男/女生多的情況出現. 這種狀態的特征就是 $b=0$ 或 $g=0$ . 所以我們可以得到狀態 $dp[i][j][b][g]$ 的轉移:

\[dp[i][j][b][g] \rightarrow dp[i+1][j][b+1][max(0,g-1)]\]

\[dp[i][j][b][g] \rightarrow dp[i][j+1][max(0,b-1)][g+1]\]

邊界為$dp[0][0][0][0]=1$

(推表大法好...最開始用填表法結果漏狀態了QAQ)

附參考實現:

GitHub

技術分享
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <iostream>
 5 #include <algorithm>
 6 
 7 const int MAXN=160;
 8 const int MAXK=30;
 9 const int MOD=12345678;
10 
11 int dp[MAXN][MAXN][MAXK][MAXK];
12 
13 int main(){
14     int n;
15     int m;
16     int k;
17     int ans=0;
18     scanf("%d%d%d",&n,&m,&k);
19     dp[0][0][0][0]=1;
20     for(int i=0;i<=n;i++){
21         for(int j=0;j<=m;j++){
22             for(int ii=0;ii<=k;ii++){
23                 for(int jj=0;jj<=k;jj++){
24                     dp[i+1][j][ii+1][std::max(0,jj-1)]=(dp[i][j][ii][jj]+dp[i+1][j][ii+1][std::max(0,jj-1)])%MOD;
25                     dp[i][j+1][std::max(0,ii-1)][jj+1]=(dp[i][j][ii][jj]+dp[i][j+1][std::max(0,ii-1)][jj+1])%MOD;
26                 }
27             }
28         }
29     }
30     for(int i=0;i<=k;i++){
31         for(int j=0;j<=k;j++){
32             ans=(dp[n][m][i][j]+ans)%MOD;
33         }
34     }
35     printf("%d\n",ans);
36     return 0;
37 }
Backup

[BZOJ 1037][ZJOI2008]生日聚會Party