本文作於2023年歲末,沒有寫完就決定2023年不搞oi了。此文暫且存放在這裡,待我有大學上之後,將決定是否開始維護這個部落格。這裡以後將代替林影閣。
混合揹包
揹包是個很經典而實用的問題……
總體上,揹包可以分為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)
【樣例輸入】
10500 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,其中0<=k<=w。它的最優子結構很好證明。看吧,我們沒有以物品為物件。這種最優解操作是在函式層面上的。也就是說,為了得到節點p的函式值fp(v),則它的孩子的f值要在這之前獲得。之後我們列舉各種空間分配的方法,找到其中的最大值。
我們就把這些物品「泛化」了,節點2、3通過函式f的形式,將自己這棵自樹的最優解情況匯報給節點1,節點1通過上述的狀態轉移方程,評測出節點1的最優解。
初始化時,所有葉子節點都做這樣的操作。設節點k的價值為v,價錢為w。fk全部賦0,然後fk[w]=v,
在程式實現上,我們要把這個森林轉化成二叉樹,這樣處理起來比較方便(同樣的比如《選課》)。
程式實現見**段2(未完成)。
**段1
program example1(input,output);
varn,m,i:longint;
v,t:array[1..1000]of
longint;
f:array[0..10000]of
longint;
iszeroone:array[1..1000]of
boolean;
chr0,chr1:char;
function max(x,y:longint):longint;
begin
if x>y then exit(x) else exit(y);
end;
procedure zeroonepack(cost,value:longint);
varvi:longint;
begin
for vi:=m downto cost do
f[vi]:=max(f[vi],f[vi-cost]+value);
end;
procedure completepack(cost,value:longint);
varvi:longint;
begin
for vi:=cost to m do
f[vi]:=max(f[vi],f[vi-cost]+value);
end;
begin
assign(input,'a.in'); reset(input);
readln(n,m);
for i:=1
to n do
begin
readln(v[i],t[i],chr0,chr1);
if chr1='a'
then iszeroone[i]:=true
else iszeroone[i]:=false;
end;
fillchar(f,sizeof(f),0);
for i:=1
to n do
if iszeroone[i] then zeroonepack(t[i],v[i])
else completepack(t[i],v[i]);
writeln(f[m]);
close(input);
end.
**段2(未完成)
混合揹包 分組揹包
啥是混合揹包呢,就是包含01揹包,完全揹包,多重揹包。有n件物品,揹包承重最大為m,w i 代表重量,v i 代表價值,s i 代表種類。s i 1,可用1次,s i 0,可用無限次,s i 0,可用s i 次.解法 見 const int maxn 1e4 5 int n,m,dp m 1 w n...
分組揹包,混合揹包,有依賴的揹包
一.分組揹包 每組只能選乙個 所謂分組揹包,就是把物品分成n組,每組裡面m個物品,從這n組中每組選乙個物品,使得在揹包體積是v的條件下價值最大 思路 對於每一組由於只能選乙個,所以就是決策這一組中選哪乙個獲得的價值最大 for int i 1 i n i 列舉這是第幾組 num i 代表第i組物品的...
0 1 完全揹包 多重揹包問題 混合揹包
0 1揹包問題 dp i j 表示前i件物品,體積容量為j的揹包所能獲得的最大價值 決策是第i個物品選不選 轉移方程 dp i j max dp i 1 j dp i 1 j v i w i n為物品數量,m為揹包體積 for int i 1 i n i for int j 0 j m j if j...