bzoj1037: [ZJOI2008]生日聚会Party
DP,状态为f[i][j][k1][k2],表示第i个人,用了j个男孩,后缀中男生比女生多的数量最大值为k1,女生比男生多的数量最大值为k2.k1,k2<=k.
状态转移方程f[i][j][k1][k2] = f[i-1][j-1][k1+1][k2-1] + f[i-1][j][k1-1][k2+1]。前提都合法。
答案为 f[n+m][n][k1][k2]. 0<=k1,k2<=n.
需要用滚动数组,最后推出的next,因为swap一下,变成了cur,所以输出的是cur.
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int MOD = 12345678; const int maxn = 150 + 10; const int maxk = 20 + 10; long long f[2][maxn][maxk][maxk],res; int n,m,k,cur,next; void alter(long long &x,long long y) { x = (x+y)%MOD; } int main() { scanf("%d%d%d",&n,&m,&k); cur = 0,next = 1; f[0][0][0][0] = 1; for(int i = 0; i < (n+m); i++,swap(cur,next)) { memset(f[next],0,sizeof(f[next])); for(int j = 0; j <= min(i,n); j++) for(int k1 = 0; k1 <= min(k,n); k1++) for(int k2 = 0; k2 <= min(i-j,k); k2++){ if(k1 < k && j < n) { alter(f[next][j+1][k1+1][max(k2-1,0)],f[cur][j][k1][k2]); } if(k2 < k && i-j < m) { alter(f[next][j][max(k1-1,0)][k2+1],f[cur][j][k1][k2]); } } } for(int i = 0; i <= k; i++) for(int j = 0; j <= k; j++) alter(res,f[cur][n][i][j]); printf("%lld\n",res); return 0; }