JZOJ1517.2017.05.27【提高组】模拟赛C组 T1背包问题

本文介绍了一种经典的背包问题解法,通过对每组物品选择连续段进行优化,利用动态规划减少时间复杂度,并通过预处理简化计算过程。

Description

从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

Sample Output

6

Data Constraint

Hint

【样例说明】
第1组选6、8,第2组选80,第3组不选。
【限制】
60%的数据满足:1 <= ni <= 10
100%的数据满足:1 <= ni <= 100,1<=v<=5000,1<=T<=10

思路:
这题比较经典,可以列入背包问题的例题
之前有一段时间没做背包,比赛时推了十分钟左右,然后开码

从一开始就在思考DP的做法
题目中限制的取法,只需再加上两个l,r循环枚举头尾
那么思考状态
一个状态明显不能满足要求(背包容量的状态)
于是乎,我们可以考虑加上另一个第几层的状态:

f[i,j]表示到取到第j层时容量为i的背包装入的最大值
转移方程为f[i,j]=max(f[i,j],f[i-x,j-1]+x)
无需任何初始化
这里的x为当前这一段物品要取的和,用前缀和求即可(x=pre[j,r]-pre[j,l-1])
然后AC
简单吧?

哦,对了,还有一个东西
我们可以在DP之前先把每一层的所有情况的数字存到另一个数组里
DP中使用一重循环代替枚举头尾l,r的两重循环
由此可以将四重循环压至三重
code好看了,时间也砍了100ms左右

代码:

var
        f:array[0..5000,0..10]of longint;
        pre:array[0..10,0..100]of longint;
        fx:array[0..10,0..100000]of longint;
        a,b:array[0..10]of longint;
        n,i,j,k,l,r,v,t,tot,ans,x:longint;
function max(x,y:longint):longint;
begin
        if x>y then exit(x);
        exit(y);
end;
function min(x,y:longint):longint;
begin
        if x<y then exit(x);
        exit(y);
end;
begin
        readln(v,t);
        for i:=1 to t do
        begin
                read(a[i]);
                for j:=1 to a[i] do
                begin
                        read(x);
                        pre[i,j]:=pre[i,j-1]+x;
                end;
        end;
        for i:=1 to t do
        for l:=1 to a[i] do
        for r:=l to a[i] do
        begin
                inc(b[i]);
                fx[i,b[i]]:=pre[i,r]-pre[i,l-1];
        end;
        for i:=1 to v do
        for j:=1 to t do
        begin
                f[i,j]:=f[i,j-1];
                for k:=1 to b[j] do
                begin
                        if fx[j,k]<=i then
                        f[i,j]:=max(f[i,j],f[i-fx[j,k],j-1]+fx[j,k]);
                end;
        end;
        for i:=1 to t do
        ans:=max(ans,f[v,i]);
        writeln(v-ans);
end.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值