码迷,mamicode.com
首页 > 其他好文 > 详细

20180516模拟赛T3——bag

时间:2018-05-16 22:36:50      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:long   01背包   注释   中间   删除   write   简单   证明   ges   

技术分享图片

技术分享图片

题解

Cqz大佬在代码上的注释:

前i个物品,做成体积为j的东西,有多少种方案数

后i个物品,做成体积为j的东西,有多少种方案书(大佬打错了)

两个DP数组合并。 做不到?

其实就是把中间那段切断,然后把左右两边合并。

技术分享图片

贴一段代码吧:

Rep(i,1,n)
{
    Dep(j,m,v[i])
        f[i][j] = (f[i-1][j] + f[i-1][j - v[i]])%Mod;
    Dep(j,v[i]-1,0)
        f[i][j] = f[i-1][j];
}
g[n+1][0] = 1;
Dep(i,n,1)
{
    Dep(j,m,v[i])
        g[i][j] = (g[i+1][j] + g[i+1][j - v[i]])%Mod;
    Dep(j,v[i]-1,0)
        g[i][j] = g[i+1][j];
}
Rep(i,1,n)
{
    ll ans = 0;
    Rep(j,0,m)
        ans = (ans + (1ll * f[i-1][j] * g[i+1][m-j] % Mod)) % Mod;//注意合并阶段一定要开long long
    writeln(ans);
}

zd大佬&sxd大佬的做法

先跑一遍01背包,要删除一段时,倒着跑一遍。请教了两位大佬,然而好像都无法证明。但在感性上应该是可以理解的(极其具有对称性)。

这个的代码就简单多了:

dp[0] = 1;
for(int i = 1; i <= n; ++i)
    for(int j = m; j >= v[i]; --j)
        dp[j] = (dp[j]+dp[j-v[i]])%mod;
for(int i = 1; i <= n; ++i)
{
    for(int j = 0; j <= m; ++j)
        f[j] = dp[j];
    for(int j = v[i]; j <= m; ++j)
        f[j] = ((f[j]-f[j-v[i]])%mod+mod)%mod;
    writeln(f[m]);
}

听说yyh大佬有一种容斥做法,然而我看不懂啊……只好直接贴代码了:

f[0]=1;
for(LL i=1; i<=n; i++)
{
    a[i]=read();
    for(LL j=m; j>=a[i]; j--)
        (f[j]+=f[j-a[i]])%=md;
}
for(LL i=1; i<=n; i++)
{
    ans=f[m];
    for(LL j=1; j*a[i]<=m; j++)
    {
        if(j&1)
            (ans-=f[m-j*a[i]])%=md;
        else
            (ans+=f[m-j*a[i]])%=md;
    }
    printf("%lld\n",(ans%md+md)%md);
}

20180516模拟赛T3——bag

标签:long   01背包   注释   中间   删除   write   简单   证明   ges   

原文地址:https://www.cnblogs.com/pfypfy/p/9048088.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!