混合背包与泛化背包(未完成)

本文作于2007年岁末,没有写完就决定2008年不搞OI了。此文暂且存放在这里,待我有大学上之后,将决定是否开始维护这个博客。这里以后将代替林影阁

 

今年NOIP前就曾读过dd的《背包问题九讲》,时间仓促,只程序实现了前4讲。觉得混合背包这个设想,以及泛化背包这个概念很经典,有必要写点东西共享一下自己的看法。新年之际献上。Happy New Year~

 

混合背包
背包是个很经典而实用的问题……
总体上,背包可以分为4类。部分背包(可以用贪心有效解决),01背包,完全背包,多重背包(这三个可以用DP有效解决)。而混合背包是后三者的混合。这几个的理论在《背包问题九讲》中有过详细介绍。这里我们只通过一个实例来看混合背包是如何实现的。

 

例1 期中考试前夜
【问题描述】
快乐的DW今天很不高兴,因为明天要期中考试,而且她知道她复习不完了。
【问题描述】
虽然形势不很乐观,但DW还是希望明天能拿到尽可能高的分数。
DW简单分析了一下,现在可利用的时间为m,还有n个任务需要完成。第i个任务需要一定的时间t(i),但也有一定的价值v(i)。
但是好像没这么简单。她发现所有的任务可以分为两类,A类和B类。
A类是背诵类的任务。如果第i个任务是A类的,那么第一遍做这个任务获得价值v(i),再做它的价值就是0。也就是说,第i个任务做x次的价值就是v(i) (x>0)。
B类是练习类的任务。如果第i个任务是B类的,那么每做一遍这个任务获得的价值都是v(i)。也就是说,第i个任务做x次的价值就是x*v(i)。
DW希望她能利用这些时间获得最大的价值,而坐在她后面的ZS也在忙着复习(是复习NOIP,不是期中考试),所以请聪明的你来帮忙了。
【输入格式】
第一行两个整数,n,m。含义已述。
接下来有n行,第i+1行有三个量v,t,chr。V,t分别表示v(i),t(i)。chr是字符A或B,表示它所属的任务类别。
【样例输入】
5 10
2 3 A
2 4 A
2 5 A
2 6 A
5 2 B
【输出格式】
一行,一个整数,表示可以获得的最大价值。
【样例输出】
25
【数据规模】
n<=1000,m<=10000,其它数据<=maxlongint。

可以看出这是01背包和完全背包的混合。见代码段1。

 

泛化背包
背包中放入的物品可能存在其它限定。比如,要放入物品a,物品b也必须放入。称这种关系为依附关系。《金明的预算方案》是一个典例,不过它的依附方式很简单,对于每一种依附情况枚举即可。
泛化背包这个概念是由dd牛提出,用来解决更一般的具有依附关系的背包问题。

 

例2 金明的预算方案改
【问题描述】
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了。
物品之间是存在依附关系的,下表就是一些例子:

 

如果要买一件物品,如果它有父节点,那么它的父节点也一定要买。比如。要买扫描仪就必须买电脑,那么买电脑也就必须买桌子。所有的依附关系都可以由一个森林来描述,这意味着不可能出现“依附环”。
金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)请你帮助金明设计一个满足要求的购物单。
【输入格式】
输入文件的第1行,为两个正整数,用一个空格隔开:
N m
其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)
从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数
v p q
(其中v表示该物品的价格(v<10000),p表示该物品的重要度(1~5),q表示该物品的直接依附物品。如果它没有依附的物品,那么q=0)
【样例输入】
10
500 0 1
5000 1 4
300 1 5
1000 2 2
800 2 4
10 0 1
10 6 3
10 6 2
200 0 3
800 9 3
(注:这个样例与图中物品的标记一一对应)
【输出格式】
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值
(<200000)。

下面我们来讨论这个问题。
请设想,如果你就是那个最优解,你是否真的关心放在背包中的是哪些物品呢?可能并不是,你所关心(或担心)的是,备选物品是否利用背包的空间组合出了最优解。
我们规定节点k由一个函数fk表示(请将k理解为函数f的下标),fk(w)表示这样的含义:提供空间w,将以k为根节点的子树中的节点作为物品选择对象,组合出的最优价值。
如果要求f1,那么我们会想知道f2和f3,那么f1(w)=max{f2(k)+f3(w-k)},其中0<=k<=w。它的最优子结构很好证明。看吧,我们没有以物品为对象。这种最优解操作是在函数层面上的。也就是说,为了得到节点p的函数值fp(v),则它的孩子的f值要在这之前获得。之后我们枚举各种空间分配的方法,找到其中的最大值。
我们就把这些物品“泛化”了,节点2、3通过函数f的形式,将自己这棵自树的最优解情况汇报给节点1,节点1通过上述的状态转移方程,评测出节点1的最优解。
初始化时,所有叶子节点都做这样的操作。设节点k的价值为v,价钱为w。fk全部赋0,然后fk[w]=v,

在程序实现上,我们要把这个森林转化成二叉树,这样处理起来比较方便(同样的比如《选课》)。
程序实现见代码段2(未完成)。

代码段1

  1. program example1(input,output);
  2. var
  3. n,m,i:longint;
  4. v,t:array[1..1000]of longint;
  5. f:array[0..10000]of longint;
  6. IsZeroOne:array[1..1000]of boolean;
  7. chr0,chr1:char;
  8. function max(x,y:longint):longint;
  9. begin
  10. if x>y then exit(x) else exit(y);
  11. end;
  12. procedure ZeroOnePack(cost,value:longint);
  13. var
  14. vi:longint;
  15. begin
  16. for vi:=m downto cost do
  17.    f[vi]:=max(f[vi],f[vi-cost]+value);
  18. end;
  19. procedure CompletePack(cost,value:longint);
  20. var
  21. vi:longint;
  22. begin
  23. for vi:=cost to m do
  24.    f[vi]:=max(f[vi],f[vi-cost]+value);
  25. end;
  26. begin
  27. assign(input,'a.in'); reset(input);
  28. readln(n,m);
  29. for i:=1 to n do
  30. begin
  31.     readln(v[i],t[i],chr0,chr1);
  32.     if chr1='A' then IsZeroOne[i]:=true else IsZeroOne[i]:=false;
  33. end;
  34. fillchar(f,sizeof(f),0);
  35. for i:=1 to n do
  36.    if IsZeroOne[i] then ZeroOnePack(t[i],v[i])
  37.                    else CompletePack(t[i],v[i]);
  38. writeln(f[m]);
  39. close(input);
  40. end.

代码段2(未完成)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值