洛谷 P1782 旅行商的背包(二进制优化下的DP)

来源:互联网 发布:淘宝店自动回复短语 编辑:程序博客网 时间:2024/06/05 07:22

题目描述

小S坚信任何问题都可以在多项式时间内解决,于是他准备亲自去当一回旅行商。在出发之前,他购进了一些物品。这些物品共有n种,第i种体积为Vi,价值为Wi,共有Di件。他的背包体积是C。怎样装才能获得尽量多的收益呢?作为一名大神犇,他轻而易举的解决了这个问题。

然而,就在他出发前,他又收到了一批奇货。这些货共有m件,第i件的价值Yi与分配的体积Xi之间的关系为:Yi=ai*Xi^2+bi*Xi+ci。这是件好事,但小S却不知道怎么处理了,于是他找到了一位超级神犇(也就是你),请你帮他解决这个问题。

输入输出格式

输入格式:
第一行三个数n,m,C,如题中所述;

以下n行,每行有三个数Vi,Wi,Di,如题中所述;

以下m行,每行有三个数ai,bi,ci,如题中所述。

输出格式:
仅一行,为最大的价值。

输入输出样例

输入样例#1:
2 1 10
1 2 3
3 4 1
-1 8 -16
输出样例#1:
10
说明

【数据范围】

对于100%的数据,1≤n≤10,000,1≤m≤5,1≤C≤10000,

1≤Wi,Vi,Di≤1000,-1000≤ai,bi,ci≤1000.

【样例解释】

前两种物品全部选走,最后一个奇货分给4的体积,收益为2*3+4*1+-1*16+8*4+-16=10。

思路:抛开后面的奇货,就是个混合背包,但数据范围较大,因此要用一些方法优化,我个人能想到的是二进制优化(应该有大佬会其他方法),而后面的奇货较小,可以直接暴力枚举。

二进制思想
假设有1000个苹果,现在要取n个苹果,如何取?
正常的做法应该是将苹果一个一个拿出来,直到n个苹果被取出来。

又假设有1000个苹果和10只箱子,如何快速的取出n个苹果呢?
可以在每个箱子中放 2^i (i<=0<=n)个苹果,也就是 1、2、4、8、16、32、64、128、256、489(最后的余数)
相当于把十进制的数用二进制来表示,取任意n个苹果时,只要推出几只箱子就可以了。

二进制优化的思路,就是将M个相同物品,堆分成1、2、4、8、…这“几个”大小不等的物品
通过对这几个堆的取舍,来完成达到拿走n个物品的的最优情况

例如:15=8+4+2+1(只要取四次)

program df;
var i,j,n,m,x,y,z,k,t,kk:longint;
a,b,c,d,f:array[0..100000] of longint;

procedure check1(x,y:longint); //完全背包
var i:longint;
begin
for i:=x to k do
if f[i-x]+y>f[i] then f[i]:=f[i-x]+y;
end;

procedure check2(x,y:longint); //01背包
var i:longint;
begin
for i:=k downto x do
if f[i-x]+y>f[i] then f[i]:=f[i-x]+y;
end;

procedure deal(v,w,c:longint);
var i,j,kk:longint;
begin
if c*v>k then begin check1(v,w); exit; end;
kk:=1;
while c>kk do
begin
check2(v*kk,w*kk); //每次去2的(kk-1)次方
c:=c-kk;
kk:=kk*2;
end;
check2(v*c,w*c); //最后剩余的再放入;
end;

begin
readln(n,m,k);
for i:=1 to n do
begin
readln(x,y,z);
deal(x,y,z);
end;
for i:=1 to m do
begin
readln(x,y,z);
for j:=k downto 0 do
for kk:=0 to j do
if f[j-kk]+(x*kk+y)*kk+z>f[j] then f[j]:=f[j-kk]+(x*kk+y)*kk+z;
end;
writeln(f[k]);
end.

原创粉丝点击