Description
给定一个01背包和n个物品,求有多少种选择方法使得背包再也放不下余下的任意物品。
Input
第一行一个数字q,表示有q组测试数据
对于每一组测试数据第一行有两个数字n和m,n表示物品的个数,m表示背包的体积,下一行中有n个数字V1…Vn分别表示每件物品的体积。
Output
一个数字,sum表示不能使背包再放入任何物品的方案数。(保证不大于int范围)
Sample Input
2
6 25
8 9 8 7 16 5
30 250
1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
Sample Output
15
16509438
Hint
对于100%的数据,q≤1000,n≤30,m≤1000;
对于每一个Vi,保证Vi≤1000;
【分析】
考虑到n很小。于是我们用枚举+Dp来A掉此题。
每次输入之后由小到大排序,然后开始枚举x。表示放不下x这个物品,又因为放不下x这个物品,所以1~x-1是一定能放进去的(已经排序)。于是我们用DP求出前x-1个都一定放进去之后,第x个放不下,后面的随便放的情况数。
f[j]表示恰好放了容积和为j的方案数。sum[]为排序后的前缀和数组。
的到状态转移方程:f[j]=sigma(f[j-V[i]) x+1<=i<=n,sum[x-1]+V[i]<=j<=m
边界:f[sum[x-1]]=1 因为前x-1个物品已经放进去了
ans=sigma(f[j]) m+1-V[x]<=j<=m
时间复杂度为O(T*(n^2)*m),严格的极限数据可能会超时,但是这个算法能够AC。
【代码】
/************************
ID:Ciocio
LANG:C++
DATE:2013-12-23
TASK:Bag
************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define MAXN 35
#define MAXM 1005
int N,M,V[MAXN],f[MAXM],Sum[MAXN];
void _read(int &x)
{
char tt=getchar();
while(tt<'0'||'9'<tt) tt=getchar();
for(x=0;'0'<=tt&&tt<='9';x=(x<<3)+(x<<1)+tt-'0',tt=getchar());
}
void _init()
{
_read(N);_read(M);
for(int i=1;i<=N;i++) _read(V[i]);
sort(V+1,V+N+1);
for(int i=1;i<=N;i++) Sum[i]=Sum[i-1]+V[i];
}
void _Dp(int x)
{
memset(f,0,sizeof(f));
f[Sum[x-1]]=1;
for(int i=x+1;i<=N;i++)
for(int j=M;j>=V[i]+Sum[x-1];j--) //每个物品只有一个,DP时逆向扫描
f[j]+=f[j-V[i]];
}
void _solve()
{
int ans=0;
for(int i=1;i<=N;i++)
{
if(Sum[i-1]>M) break; //重要
_Dp(i);
for(int j=M+1-V[i];j<=M;j++) ans+=f[j];
}
cout<<ans<<endl;
}
int main()
{
int Case;
_read(Case);
while(Case--)
{
_init();
_solve();
}
return 0;
}