POJ3093 Margaritas on the River Walk
原题地址:http://poj.org/problem?id=3093
题意:
T组数据,每次给定N个物品的价格和总的钱数,每种物品只能选一次,求共有多少种方案,使之不能再装下任一物品。
数据范围
T<=1000,N<=30,M<=1000
题解:
(好题)
因为要求背包不能再装的方案数,不能再装即=最小的也装不进去。
于是,为了避免统计重复方案,枚举不在背包中的最小的物品。那么比他小的物品必然全部在背包中,这时用剩下的容积v对比他大的物品做背包,统计方案,则此时的方案是f[v-w[i]+1]~f[v]的方案数之和。
这样的复杂度是O(TMN^2)
考虑原本做背包的过程,外面一层循环是物品,里面是容积,
那么我们实际不需要做N次背包,只需从大到小地向背包中添加物品,每次添加前如上述方式统计方案数即可。
总复杂度O(TNM)
(关键点倒不是这个优化,是如何由“判重的标准”想到“枚举不在背包中的最小物品”,这个转点在于”不能再装“意味着最小的也装不进去。)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
const int N=1005;
const int inf=0x3f3f3f3f;
int T,n,m,sum[N],f[N],w[N];
int main()
{
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
memset(f,0,sizeof(f));
memset(sum,0,sizeof(sum));
f[0]=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
sort(w+1,w+n+1);
if(w[1]>m) {printf("%d 0\n",t); continue;}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+w[i];
int ans=0;
for(int i=n;i>=1;i--)
{
if(m-sum[i-1]>=0)
{
int st=max(m-sum[i]+1,0);
for(int j=st;j<=m-sum[i-1];j++)
ans+=f[j];
}
for(int j=m;j>=w[i];j--) f[j]+=f[j-w[i]];
}
printf("%d %d\n",t,ans);
}
return 0;
}