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;
}
posted @ 2016-04-13 23:19  invoid  阅读(151)  评论(0编辑  收藏  举报