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 1Sample Output
1HINT
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] ightarrow dp[i+1][j][b+1][max(0,g-1)]]
[dp[i][j][b][g] ightarrow dp[i][j+1][max(0,b-1)][g+1]]
边界为$dp[0][0][0][0]=1$
(推表大法好...最开始用填表法结果漏状态了QAQ)
附参考实现:
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 ",ans); 36 return 0; 37 }