[DP]背包问题

该博客探讨了一种背包问题,其中必须从每组物品中选取连续一段来填充背包,并求解剩余空间的最小值。博主分析了使用动态规划解决此类问题的思路,包括建立状态转移方程和注意的细节,如预处理、初始化和状态继承问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
从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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值