poj3093 Margaritas on the River Walk(特殊的01背包)

xiaoxiao2021-02-28  131

题意:多组输入,每组给定物品数(<=30)和背包容量(<=1000)以及接下来每个物品的体积,问有多少种方案,使得装入一些物品后,无法装入剩下的任意一个物品。 可以转化成01背包来做,首先按体积从小到大排序,枚举“剩下的物品”中体积最小的。剩下的物品中体积最小的为i时,前i-1个物品是必然被装入背包的,然后对第i+1到第n个物品做01背包,此时背包容量应为[m-v[i]+1,m] (保证连此时剩下的最小的i物品都放不下),这样的话,由于第一次对(2,n)做01背包,第二次对(3,n)做01背包,每次都不得不重新做,复杂度是 O(n2m) 的。 我们可以考虑如下优化:我们能不能不每次都重做01背包?我们考虑倒着做,即 i从n到1,每次i为剩下的物品中体积最小的,则(1..i-1)应该都已经放在背包中了。而我们要对(i+1..n)做01背包。下次就是对(i..n)做01背包。发现没有!我们只是又多了一个物品i!这样我们就不需要每次都重做01背包了。我们只需每次在统计完答案后,把第i个物品放进背包。则下一次背包就已经是做好了的!这样就是 O(nm) 的了。

#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define N 1005 int v[31],n,tst,m,sum,f[N]; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x*f; } int main(){ // freopen("a.in","r",stdin); tst=read(); for(int tt=1;tt<=tst;++tt){ n=read();m=read();sum=0;ll ans=0; for(int i=1;i<=n;++i) v[i]=read(),sum+=v[i]; std::sort(v+1,v+n+1); memset(f,0,sizeof(f));f[0]=1; for(int i=n;i>=1;--i){ sum-=v[i];if(v[i]>m) continue; for(int j=0;j<v[i]&&m-sum-j>=0;++j)//j表示还差j装满m-sum。保证装不下v[i] ans+=f[m-sum-j]; for(int j=m;j>=v[i];--j) f[j]+=f[j-v[i]]; } printf("%d %lld\n",tt,ans); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-66556.html

最新回复(0)