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 1

Sample Output

1

HINT

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;
}

  

posted @ 2017-06-12 06:33  _rqy  阅读(257)  评论(0编辑  收藏  举报