BZOJ 1307 [ZJOI2008]生日聚会Party - DP

为啥机房的巨佬都说这是一个水题。。。绝望ing
/一个尴尬而不失友好的微笑

想到解法了,关键不知如何递推,其实很好递推啊,是我太愚蠢了。。。
开始想法:dp[i][j][k][l]其中i为男生数,j为女生数,k为所有后缀中女生-男生的最大值,l为所有后缀中男生-女生的最大值。

后来发现一件事情就是,设男生人数和女生人数的话循环变量不好写,dp方程中同时出现i-1和j-1,得写记忆化搜索(原谅我的菜),于是就改i的含义,改为总共i个人,其中女生j人,男生(i-j)人,dp方程很好写。

这里还有一个问题,就是如果采用后缀不超过k的方法,怎么保证中间部分不会存在不合法的情况呢?这个很好解决,因为一个串是一位位递推上来的,不合法的情况在处理到那一位已经被判成不合法了,不会累加到之后的状态上了。

然后强调一下max(x-1,0)这一句,怎么保证负数强制改为0,总答案是对的呢?原因是x为负数,则y会限制情况。假设x未更改为0,其上升时y必然下降,且下降的高度不会超过k,而现在x设为0后上升的高度也绝对不会超过k,所以每一个状态的种数是错的,而总答案可以保证是不变的。

最后分析一下这道题使用填表法为什么是错的。原因是上面阐述的max的问题,强制max转换时单向的,是前推向后,而后推向前肯定是错的啦(我个智障哎。。。)

update:2017/11/10
雾。。。发现自己就是一个智障。强制取max转成非负是因为总有一个空的后缀保证最大值不小于0

代码来自OTZ@27rabbit

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=155;
const int maxm=25;
const int mod=12345678;

int n,m,k;
int dp[maxn<<1][maxn][maxm][maxm];

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<=min(m,i);j++)
        {
            for(int x=0;x<=min(k,j);x++)//女生减男生 
            {
                for(int y=0;y<=min(k,i-j);y++)//男生减女生 
                {
                    if(x<k)
                        dp[i+1][j+1][x+1][max(y-1,0)]=(dp[i+1][j+1][x+1][max(y-1,0)]+dp[i][j][x][y])%mod;
                    if(y<k)
                        dp[i+1][j][max(x-1,0)][y+1]=(dp[i+1][j][max(x-1,0)][y+1]+dp[i][j][x][y])%mod;
                }
            }
        }
    }
    long long ans=0;
    for(int i=0;i<=k;i++)
        for(int j=0;j<=k;j++)
            ans=(ans+dp[n+m][m][i][j])%mod;
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值