【51nod1597】【DP】有限背包计数问题

Description

你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少
两种方案不同当且仅当存在至少一个数i满足第i种物品使用的数量不同

Input

第一行一个正整数n
1<=n<=10^5

Output

一个非负整数表示答案,你需要将答案对23333333取模

Input1

3

Output1

2

Input2

233

Output2

1167892

DP,一开始没什么思路,然后研究了很久终于会做了,感觉我DP超弱


先是分成两部分,大于 n 和小于 n 的,小于的部分是有限制的背包,大于的部分是完全背包(因为你用不完),我们就可以分成两部分DP,两部分都不难,但是滚动再加上前缀和我就懵逼了,前面就是前缀和加滚动,后面是个序列DP。


前面dp[i][j]表示前i个数,体积为j,然后对于同一个i就是加上它的前缀和,然后滚一下i就好了
后面可以分为两种操作,一个是加一个m+1这个数,一个所有数加一
然后再乘法原理就好了


但是完全不知道为什么我原先写那个(注释里面的)在本机上测全部过,但是交上去就各种WA,样例都要WA,完全不知道为什么,是不是什么编译器的问题

然后下面是我的代码,注释掉的是我原来写的,注释后面的是网上抄的,完全不知道为什么我的交上去要WA

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define ll long long
#define clr(x)  memset(x,0,sizeof(x))
#define maxclr(x)  memset(x,127,sizeof(x))
#define N 320
#define M 100005
#define P 23333333LL

using namespace std;

int dp[2][M],tmp[M],g[N][M],n,m,ans;
ll g1[M],f1[M];
int now,pre;

int main()
{
    freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    scanf("%d",&n);
    m=ceil(sqrt((double)n));
    dp[0][0]=g[0][0]=1;
    for(int i=0;i<=m;i++)
    {
        clr(tmp);
        for(int j=0;j<=n;j++)
        {
            if(i)
            {
                int mod=j%i;
                now=i&1;pre=1^(i&1);
                tmp[mod]=(tmp[mod]+dp[pre][j])%P;
                dp[now][j]=tmp[mod];
                if(j>=i*i)
                    tmp[mod]=(tmp[mod]-dp[pre][j-i*i]+P)%P;
            }

            if(i&&j>=i)g[i][j]=(g[i][j]+g[i][j-i])%P;
            if(j>m)g[i][j]=(g[i][j]+g[i-1][j-m-1])%P;
        }
    }
    /*  
    for(int i=0;i<=m;i++)
        for(int j=0;j<=n;j++)
            ans=((ll)ans+(ll)dp[now][j]*(ll)g[i][n-j])%P;
    */
    ++g1[0];
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            (g1[i]+=g[j][i])%=P;
    for (int i=0;i<=n;++i)f1[i]=dp[now][i];
        for (int i=0;i<=n;++i)
            (ans+=(f1[i]*g1[n-i]%P))%=P;
    cout<<ans<<'\n';
    return 0;
}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值