题目描述
从T组物品中选出一些物品,放入背包中,求剩余空间的最小值。
限制条件:从每组物品中挑选物品必须要选取连续的一段。就是说,如果这组物品共有n个: 物品1、物品2、物品3、…、物品n,那么只能选取物品i、物品i+1、…、物品j,其中1<=i<=j<=n,或者不选。
Input
第一行为两个用空格隔开的正整数v和T。表示背包的空间和物品的组数。接下来有T行,每行先是一个正整数ni,表示这组物品有ni个,然后ni个正整数,表示每个物品的大小。
Output
仅一个数,表示剩余空间的最小值。
Sample Input
100 3
3 7 6 8
2 80 70
4 101 108 103 150
100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10
分析
这题一开始没什么好思路啊
我是想到DP,可是我的DP是这样的:
建fi,j,表示前i组选了j个的最大重量。
可是后来发现如果中间有多组或任意一组数据所有物品都大于v,那么状态无法继承下来
然后后来又建布尔数组fi,j,表示前i组的物品能否达成j的重量
首先这种做法的实现需要预处理,要先处理一个前缀和出来,再用n^2枚举这个前缀和所有可能的值(就是一段连续物品所有可能的值)并排序【这一步可以省去,因为剪枝与否不影响正确了。】
于是三重循环,
第一重i:枚举组
第二重j:枚举组中所有可能的值
第三重k:枚举上一组所有可能的值(上限为v-当前组所枚举的值)
然后转移方程是这样的:
f[i][k+b[i][j]]=f[i-1][k];
还有两个注意事项:
1、所有组无论物品多少都可以满足fi,0≡1,要预处理
2、千万千万不要忘记无论物品多寡重量与否,都可以组成重量为0(用于继承第一个想法所做不到的地方)【此处本人掉了0.09%的正确率】
#include <iostream>
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int v,t,ans;
int n[11],s[11][101],a[11][101],b[11][10001];
bool f[11][5001];
int i,j,k,q;
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
scanf("%d%d",&v,&t);
for (i=1;i<=t;i++)
{
scanf("%d",&n[i]);
for (j=1;j<=n[i];j++)
{
scanf("%d",&s[i][j]);
a[i][j]=a[i][j-1]+s[i][j];
}
q=0;
for (j=1;j<=n[i];j++)
for (k=j;k<=n[i];k++)
{
q++;
b[i][q]=a[i][k]-a[i][j-1];
}
q++;
b[i][0]=q;
sort(b[i]+1,b[i]+1+q,cmp);
}
for (i=0;i<=t;i++) f[i][0]=1;
for (i=1;i<=t;i++)
for (j=1;j<=b[i][0];j++)
{
for (k=0;k<=v-b[i][j];k++)
if (f[i-1][k]) f[i][k+b[i][j]]=1;
}
for (i=1;i<=t;i++)
for (j=v;j>=0;j--)
if (f[i][j])
{
ans=max(ans,j);
break;
}
printf("%d",v-ans);
}