BZOJ 1037: [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 1

Sample Output

1

HINT

n , m ≤ 150,k ≤ 20。

Solution

dp[i][j][k][l]表示前i个人,选了j个男孩,其中最大的一段男孩比女孩多k人,最大的一段女孩比男孩多l人的方案数。

(i,j,k,l)->(i+1,j+1,k+1,max(l-1,0)) (k+1<=K)和(i+1,j,max(k-1,0),l+1) (l+1<=K)

一开始顺着推,用(i-1,j-1,max(k-1,0),l+1)和(i-1,j,k+1,max(l-1))去更新状态(i,j,k,l),结果是0。想了想,是因为(i,j,k,l)依赖的状态不止两个,而(i,j,k,l)可转移到的状态只有两个。

画个图:

神奇的DP技巧!还有这题其实可以滚动数组优化,但是神奇的是,滚动数组居然可以优化时间。。。

什么鬼Σ( ° △ °|||)︴

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <cstdio>
#include <algorithm>
using namespace std;
const int mod = 12345678;
int dp[2][155][25][25], N, M, K, ans, cur = 0, nxt = 1;
inline void inc(int &x, int val) { x = (x+val)%mod; }
int main() {
    scanf("%d %d %d", &N, &M, &K);
    dp[0][0][0][0] = 1;
    for (int i = 0; i < N + M; ++i) {
        for (int j = 0; j <= N; ++j)
            for (int k = 0; k <= K; ++k)
                for (int l = 0; l <= K; ++l) {
                    if (k+1<=K) inc(dp[nxt][j+1][k+1][max(l-1,0)], dp[cur][j][k][l]);
                    if (l+1<=K) inc(dp[nxt][j][max(k-1,0)][l+1], dp[cur][j][k][l]);
                }
        swap(cur, nxt);
        for (int j = 0; j <= N; ++j)
            for (int k = 0; k <= K; ++k)
                for (int l = 0; l <= K; ++l)
                    dp[nxt][j][k][l] = 0;
    }
    for (int i = 0; i <= K; ++i)
        for (int j = 0; j <= K; ++j)
            inc(ans, dp[cur][N][i][j]);
    printf("%d", ans);
    return 0;
}