卧薪尝胆,厚积薄发。
ZJOI2008 生日聚会
Date: Fri Oct 12 21:32:32 CST 2018
In Category:
NoCategory
Description:
一个
$01$
序列,要求任何一个区间
$0$
和
$1$
的个数差不超过
$k$
,求方案数。
$1\leqslant n\leqslant 150,1\leqslant k\leqslant 20$
Solution:
一个通用的优化思路是考虑一个计算是不是有冗余,对于这个,其实发现我们只要计算每个后缀的个数就行了,因为已经结束的肯定不用考虑,又思考了一下发现只有男生和女生差的最大值是有用的,因为较小的在以后也不会成为最大的,于是设状态为
$f[i][j][k][l]$
表示
$i$
个男生
$j$
个女生,男生比女生最大多
$k$
,女生比男生最大多
$l$
的方案数,有了这么好用的状态,转移也就很好写:
最后一个放男生:
$f[i+1][j][k+1][\max(l-1,0)]+=f[i][j][k][l]$
最后一个放女生:
$f[i][j+1][\max(k-1,0)][l+1]+=f[i][j][k][l]$
之所以和
$0$
取
$\max$
是因为最后一个没有任何一个人的后缀是
$0$
,所以不会比
$0$
小。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m,d;
#define MAXN 200
#define MAXK 30
int f[MAXN][MAXN][MAXK][MAXK];
#define MOD 12345678
int main()
{
scanf("%d%d%d",&n,&m,&d);
f[0][0][0][0] = 1;
for(int i = 0;i <= n;++i)
{
for(int j = 0;j <= m;++j)
{
for(int k = 0;k <= d;++k)
{
for(int l = 0;l <= d;++l)
{
f[i + 1][j][k + 1][max(l - 1,0)] = (f[i + 1][j][k + 1][max(l - 1,0)] + f[i][j][k][l]) % MOD;
f[i][j + 1][max(k - 1,0)][l + 1] = (f[i][j + 1][max(k - 1,0)][l + 1] + f[i][j][k][l]) % MOD;
}
}
}
}
int ans = 0;
for(int i = 0;i <= d;++i)
{
for(int j = 0;j <= d;++j)
{
ans = (ans + f[n][m][i][j]) % MOD;
}
}
cout << ans << endl;
return 0;
}
In tag:
DP-计数DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡