bzoj1037 [ZJOI2008]生日聚会Party

【未解之谜】
既然有                  f[i+1][j][x+1][max(y-1,0)]+=f[i][j][x][y]
                             f[i][j+1][max(x-1,0)][y+1]+=f[i][j][x][y]
为什么不能转换成f[i][j][x][y]+=f[i-1][j][x-1][y+1]
                             f[i][j][x][y]+=f[i][j-1][x+1][y-1]
来求解呢?求助!

其实,该题的DP方式是“我为人人”,我想看看能不能换成“人人为我”的形式来做。



【题意】
假设参加party的人中共有n个男孩与m个女孩,就座的方案应满足如下条件:对于任意连续的一段,男孩与女孩的数目之差不超过k。这样的就座方案其实是很多的,由于这个数目可能很多,只要求这个数目除以12345678的余数。


【正解】
DP

设f[i][j][x][y]表示用 i个男生 和 j个女生 组成的座位,男生与女生最大差为x,女生与男生最大差为y时的方案数。
这次DP非常神奇,它不是在求该轮到该值的时候才去搜寻,提前计算好。而它所要做的就是把后面的情况作出它应有的贡献。
所以状态转移方程也十分容易想到了:
①多1个男生的情况:当x+1<=k时,f[i+1][j][x+1][max(y-1,0)]+=f[i][j][x][y]
②多1个女生的情况:当y+1<=k时,f[i][j+1][max(x-1,0)][y+1]+=f[i][j][x][y]
其中式子左边的max(y-1,0)和max(x-1,0)作用一样,防止y-1和x-1取负;
                         x+1和y+1的原因是因为当男(女)生多了1个,对应的女(男)生就会少1个。
答案为所有男生女生都排上座位后,相差不超过过k的情况和。即Σf[n][m][x][y] (其中x<=k且y<=k)


【解答】
问题1:为什么i和j要从0开始?
解答1:因为上面说了,当前f[i][j][ ][ ]要做的不是更新自己,而是要更新它后面的。所以,尽管我们能直接求出f[i][0][i][0]和f[0][i][0][i]等于1,也不需要将它们初始化了,在计算中自然就求出来了。

问题2:像f[2][3][1][2]意为“2个男生 和 3个女生 组成的座位,男生与女生最大差为1,女生与男生最大差为2时的方案数”,差又是3又是5,结果是不是不存在?
解答2:不是的。如GGBGB就是f[2][3][1][2]的一种方案。



【正解代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=12345678;
 
int f[160][160][30][30];
 
int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
     
    memset(f,0,sizeof(f));
    f[0][0][0][0]=1;
     
    for(int i=0;i<=n;i++)//i个男生 
    {
        for(int j=0;j<=m;j++)//j个女生 
        {
            //求f[i][j][ ][ ]
            for(int x=0;x<=k;x++)//男-女最大为x 
            {
                for(int y=0;y<=k;y++)//女-男最大为y 
                {
                    //求f[i][j][x][y]
                    if(x+1<=k) f[i+1][j][x+1][max(y-1,0)]=(f[i+1][j][x+1][max(y-1,0)]+f[i][j][x][y])%mod;
                    if(y+1<=k) f[i][j+1][max(x-1,0)][y+1]=(f[i][j+1][max(x-1,0)][y+1]+f[i][j][x][y])%mod;
                }
            }
        }
    }
    
    int ans=0;
    for(int x=0;x<=k;x++)
    {
        for(int y=0;y<=k;y++)
        {
            ans=(ans+f[n][m][x][y])%mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}




【未解之谜的代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=12345678;
 
int f[160][160][30][30];
 
int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
     
    memset(f,0,sizeof(f));
    f[0][0][0][0]=1;
    for(int i=1;i<=n;i++) f[i][0][i][0]=1;
    for(int i=1;i<=m;i++) f[0][i][0][i]=1;
    
    for(int i=1;i<=n;i++)//i个男生 
    {
        for(int j=1;j<=m;j++)//j个女生 
        {
            //求f[i][j][ ][ ]
            for(int x=0;x<=k;x++)//男-女最大为x 
            {
                for(int y=0;y<=k;y++)//女-男最大为y 
                {
                    //求f[i][j][x][y]
                    if(y+1<=k) f[i][j][x][y]=(f[i][j][x][y]+f[i-1][j][x-1][y+1])%mod;
                    if(x+1<=k) f[i][j][x][y]=(f[i][j][x][y]+f[i][j-1][x+1][y-1])%mod;
                }
            }
        }
    }
    
    int ans=0;
    for(int x=0;x<=k;x++)
    {
        for(int y=0;y<=k;y++)
        {
            ans=(ans+f[n][m][x][y])%mod;
        }
    }
    printf("%d\n",ans);
    return 0;
}


推荐:《对动态规划DP的深入理解》http://blog.csdn.net/a_bright_ch/article/details/78880616

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值