BZOJ 1307 [ZJOI2008]生日聚会Party

来源:互联网 发布:手机淘宝流量劫持 编辑:程序博客网 时间:2024/05/18 20:04

为啥机房的巨佬都说这是一个水题。。。绝望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;}