LOJ 6089 小 Y 的背包计数问题 解题报告 (动态规划)

#6089. 小 Y 的背包计数问题

题意

有一个容量为 \(n\) 的背包 \(( n \le 10^5)\).

\(n\) 种物品, 第 \(i\) 种物品有 \(i\) 个, 体积为 \(i\).

求将背包装满的方案数.

思路

直接多重背包肯定不行, 加前缀和优化也有 \(n^2\).

考虑到, 体积大于 \(\sqrt{n}\) 的物品一定不会用完, 所以相当于一个无限背包,

对体积小于 \(\sqrt{n}\) 的物品, 做一个前缀和优化的多重背包就行了, 复杂度为 \(O(n\sqrt{n})\).

对于体积大于等于 \(\sqrt{n}\) 的物品, 直接做无限背包肯定也不行,

考虑到这些物品有一个性质 : 它们的体积是递增的, 那我们可以把无限背包的过程转化为以下两个操作,

  1. 往背包里加入一个体积为 \(\sqrt{n}\) 的物品.
  2. 把背包里所有物品的体积都 +1.

无限背包的过程其实就相当于维护了一个递增序列, 而上述的两个操作也可以构造出所有递增序列, 所以它们是等价的.

所以, 我们设 \(f[i][j]\) 为往背包中放了 \(i\) 个物品, 总体积为 \(j\) 的方案数,

  1. \(f[i][j]+=f[i-1][j-\sqrt{n}]\)
  2. \(f[i][j]+=f[i][j-1]\)

分别代表 往背包内加一个体积为 \(\sqrt{n}\) 的物品 和 把背包内所有物品体积 +1.

由于这些物品的体积都大于 \(\sqrt{n}\), 所以最多只能放 \(\sqrt{n}\) 个物品, 因此复杂度为 \(O(n\sqrt{n})\).

代码

#include<bits/stdc++.h>
using namespace std;
const int _=320+7;
const int __=1e5+7;
const int mod=23333333;
int n,t,f[__],sum[__],g[_][__],ans;
int main(){
  //freopen("x.in","r",stdin);
  cin>>n; t=ceil(sqrt(n));
  f[0]=1; sum[0]=1;
  for(int i=1;i<t;i++){
    for(int j=i;j<=n;j++) f[j]=(f[j]+f[j-i])%mod;
    for(int j=n;j>=(i+1)*i;j--) f[j]=((f[j]-f[j-(i+1)*i])%mod+mod)%mod;
  }
  g[0][0]=1;
  for(int i=1;i<=t;i++)
    for(int j=0;j<=n;j++){
      if(j>=i) g[i][j]=(g[i][j]+g[i][j-i])%mod;
      if(j>=t) g[i][j]=(g[i][j]+g[i-1][j-t])%mod;
    }
  for(int i=1;i<=t;i++)
    for(int j=0;j<=n;j++)
      g[0][j]=(g[0][j]+g[i][j])%mod;
  for(int j=0;j<=n;j++)
    ans=(ans+(long long)f[j]*g[0][n-j]%mod)%mod;
  printf("%d\n",ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/brucew-07/p/12181249.html
今日推荐