




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、第九章 動(dòng)態(tài)規(guī)劃第二節(jié) 背包問題第二節(jié) 背包問題一、01背包問題問題: 有N件物品和一個(gè)容量為V的背包。第i件物品的費(fèi)用(即體積,下同)是wi,價(jià)值是ci。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大。 基本思路:這是最基礎(chǔ)的背包問題,特點(diǎn)是:每種物品僅有一件,可以選擇放或不放。用子問題定義狀態(tài):即fiv表示前i件物品(部分或全部)恰放入一個(gè)容量為v的背包可以獲得的最大價(jià)值。則其狀態(tài)轉(zhuǎn)移方程便是:fiv=maxfi-1v,fi-1v-wi+ci。這個(gè)方程非常重要,基本上所有跟背包相關(guān)的問題的方程都是由它衍生出來的。所以有必要將它詳細(xì)解釋一下:“將前i件物品放入容量
2、為v的背包中”這個(gè)子問題,若只考慮第i件物品的策略(放或不放),那么就可以轉(zhuǎn)化為一個(gè)只牽扯前i-1件物品的問題。如果不放第i件物品,那么問題就轉(zhuǎn)化為“前i-1件物品放入容量為v的背包中”;如果放第i件物品,那么問題就轉(zhuǎn)化為“前i-1件物品放入剩下的容量為v-wi的背包中”,此時(shí)能獲得的最大價(jià)值就是f i-1v-wi再加上通過放入第i件物品獲得的價(jià)值ci。 注意fiv有意義當(dāng)且僅當(dāng)存在一個(gè)前i件物品的子集,其費(fèi)用總和為v。所以按照這個(gè)方程遞推完畢后,最終的答案并不一定是fNV,而是fN0.V的最大值。如果將狀態(tài)的定義中的“恰”字去掉,在轉(zhuǎn)移方程中就要再加入一項(xiàng)fi-1v,這樣就可以保證fNV就是
3、最后的答案。但是若將所有fij的初始值都賦為0,你會(huì)發(fā)現(xiàn)fnv也會(huì)是最后的答案。為什么呢?因?yàn)檫@樣你默認(rèn)了最開始fij是有意義的,只是價(jià)值為0,就看作是無物品放的背包價(jià)值都為0,所以對(duì)最終價(jià)值無影響,這樣初始化后的狀態(tài)表示就可以把“恰”字去掉。優(yōu)化空間復(fù)雜度 以上方法的時(shí)間和空間復(fù)雜度均為O(N*V),其中時(shí)間復(fù)雜度基本已經(jīng)不能再優(yōu)化了,但空間復(fù)雜度卻可以優(yōu)化到O(V)。先考慮上面講的基本思路如何實(shí)現(xiàn),肯定是有一個(gè)主循環(huán)i=1.N,每次算出來二維數(shù)組fi0.V的所有值。那么,如果只用一個(gè)數(shù)組f 0.V,能不能保證第i次循環(huán)結(jié)束后fv中表示的就是我們定義的狀態(tài)fiv呢?fiv是由fi-1v和fi
4、-1v-wi兩個(gè)子問題遞推而來,能否保證在推fiv時(shí)(也即在第i次主循環(huán)中推fv時(shí))能夠得到fi-1v和fi-1v-wi的值呢?事實(shí)上,這要求在每次主循環(huán)中我們以v=V.0的逆序推fv,這樣才能保證推fv時(shí)fv-wi保存的是狀態(tài)fi-1v-wi的值。偽代碼如下:for i=1.N for v=V.0 fv=maxfv,fv-wi+ci; 其中fv=maxfv,fv-wi+ci相當(dāng)于轉(zhuǎn)移方程fiv=maxfi-1v,fi-1v-wi+ci,因?yàn)楝F(xiàn)在的fv-wi就相當(dāng)于原來的fi-1v-wi。如果將v的循環(huán)順序從上面的逆序改成順序的話,那么則成了fiv由fiv-wi推知,與本題意不符,但它卻是另一
5、個(gè)重要的完全背包問題最簡捷的解決方案,故學(xué)習(xí)只用一維數(shù)組解01背包問題是十分必要的。 【例1】 0/1背包【問題描述】 一個(gè)旅行者有一個(gè)最多能用m公斤的背包,現(xiàn)在有n件物品,它們的重量分別是W1,W2,.,Wn,它們的價(jià)值分別為C1,C2,.,Cn.若每種物品只有一件求旅行者能獲得最大總價(jià)值。 【輸入格式】 第一行:兩個(gè)整數(shù),M(背包容量,M=200)和N(物品數(shù)量,N=30); 第2.N+1行:每行二個(gè)整數(shù)Wi,Ci,表示每個(gè)物品的重量和價(jià)值?!据敵龈袷健?僅一行,一個(gè)數(shù),表示最大總價(jià)值。【樣例輸入】package.in 10 4 2 1 3 3 4 5 7 9【樣例輸出】package.o
6、ut 12【解法一】設(shè)fiv表示前i件物品,總重量不超過v的最優(yōu)價(jià)值,則fiv=max(fi-1v-wi+ci,fi-1v) ;fnm即為最優(yōu)解,給出程序:#includeusing namespace std;const int maxm = 201, maxn = 31;int m, n;int wmaxn, cmaxn;int fmaxnmaxm; int max(int x,int y) xy?x:y; /求x和y最大值int main() scanf(%d%d,&m, &n); /背包容量m和物品數(shù)量n for (int i = 1; i = n; i+) /初始化循環(huán)變量,定義一個(gè)
7、變量并初始化 scanf(%d%d,&wi,&ci); /每個(gè)物品的重量和價(jià)值 for (int i=1;i 0; v-) if (wi =wi,1=i=n 。程序如下:#includeusing namespace std;const int maxm = 2001, maxn = 31;int m, n;int wmaxn, cmaxn;int fmaxm; int main() scanf(%d%d,&m, &n); /背包容量m和物品數(shù)量n for (int i=1; i = n; i+) scanf(“%d%d”,&wi,&ci); /每個(gè)物品的重量和價(jià)值 for (int i=1;
8、 i= wi; v-) if (fv-wi+cifv) fv = fv-wi+ci; printf(“%d”,fm); /f(m)為最優(yōu)解 return 0;總結(jié):01背包問題是最基本的背包問題,它包含了背包問題中設(shè)計(jì)狀態(tài)、方程的最基本思想,另外,別的類型的背包問題往往也可以轉(zhuǎn)換成01背包問題求解。故一定要仔細(xì)體會(huì)上面基本思路的得出方法,狀態(tài)轉(zhuǎn)移方程的意義,以及最后怎樣優(yōu)化的空間復(fù)雜度。 二、完全背包問題問題:有N種物品和一個(gè)容量為V的背包,每種物品都有無限件可用。第i種物品的費(fèi)用是wi,價(jià)值是ci。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大。 基本思路: 這個(gè)
9、問題非常類似于01背包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關(guān)的策略已并非取或不取兩種,而是有取0件、取1件、取2件等很多種。如果仍然按照解01背包時(shí)的思路,令fiv表示前i種物品恰放入一個(gè)容量為v的背包的最大權(quán)值。仍然可以按照每種物品不同的策略寫出狀態(tài)轉(zhuǎn)移方程,像這樣:fiv=maxfi-1v-k*wi+k*ci|0=k*wi= v。 將01背包問題的基本思路加以改進(jìn),得到了這樣一個(gè)清晰的方法。這說明01背包問題的方程的確是很重要,可以推及其它類型的背包問題。這個(gè)算法使用一維數(shù)組,先看偽代碼:for i=1.N for v=0.V fv=maxfv,fv-wi+
10、ci; 你會(huì)發(fā)現(xiàn),這個(gè)偽代碼與01背包問題的偽代碼只有v的循環(huán)次序不同而已。為什么這樣一改就可行呢?首先想想為什么01背包問題中要按照v=V.0的逆序來循環(huán)。這是因?yàn)橐WC第i次循環(huán)中的狀態(tài)fiv是由狀態(tài)fi-1v-wi遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時(shí),依據(jù)的是一個(gè)絕無已經(jīng)選入第i件物品的子結(jié)果fi-1v-wi。而現(xiàn)在完全背包的特點(diǎn)恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時(shí),卻正需要一個(gè)可能已選入第i種物品的子結(jié)果fiv-wi,所以就可以并且必須采用v= 0.V的順序循環(huán)。這就是這個(gè)簡單的程序?yàn)楹纬闪⒌牡览怼?這
11、個(gè)算法也可以以另外的思路得出。例如,基本思路中的狀態(tài)轉(zhuǎn)移方程可以等價(jià)地變形成這種形式:fiv=maxfi-1v,fiv-wi+ci,將這個(gè)方程用一維數(shù)組實(shí)現(xiàn),便得到了上面的偽代碼。 【例9-12】、完全背包問題【問題描述】 設(shè)有n種物品,每種物品有一個(gè)重量及一個(gè)價(jià)值。但每種物品的數(shù)量是無限的,同時(shí)有一個(gè)背包,最大載重量為M,今從n種物品中選取若干件(同一種物品可以多次選取),使其重量的和小于等于M,而價(jià)值的和為最大。【輸入格式】 第一行:兩個(gè)整數(shù),M(背包容量,M=200)和N(物品數(shù)量,N=30); 第2.N+1行:每行二個(gè)整數(shù)Wi,Ci,表示每個(gè)物品的重量和價(jià)值?!据敵龈袷健?僅一行,一個(gè)
12、數(shù),表示最大總價(jià)值?!緲永斎搿縦napsack.in 10 4 2 1 3 3 4 5 7 9【樣例輸出】knapsack.out max=12【解法一】設(shè)fiv表示前i件物品,總重量不超過v的最優(yōu)價(jià)值,則fiv=max(fiv-wi+ci,fi-1v) ; fnm即為最優(yōu)解?!緟⒖汲绦颉?includeusing namespace std;const int maxm = 201, maxn = 31;int m, n;int wmaxn, cmaxn;int fmaxnmaxm; int main() scanf(%d%d,&m, &n); /背包容量m和物品數(shù)量n for (int
13、i = 1; i = n; i+) scanf(“%d%d”,&wi,&ci); /每個(gè)物品的重量和價(jià)值 for (int i = 1; i = n; i+) /fiv表示前i件物品,總重量不超過v的最優(yōu)價(jià)值 for (int v = 1; v = m; v+) if (v fiv-wi+ci) fiv = fi-1v; else fiv = fiv-wi+ci; printf(max=%d,fnm); / fnm為最優(yōu)解 return 0; 【解法二】本問題的數(shù)學(xué)模型如下: 設(shè) f(v)表示重量不超過v公斤的最大價(jià)值, 則 f(v)=maxf(v),f(v-wi)+ci(v=wi ,1=i=
14、n) ?!緟⒖汲绦颉?includeusing namespace std;const int maxm=2001,maxn=31;int n,m,v,i;int cmaxn,wmaxn;int fmaxm;int main() scanf(%d%d,&m,&n); /背包容量m和物品數(shù)量n for(i=1;i=n;i+) scanf(%d%d,&wi,&ci); for(i=1;i=n;i+) for(v=wi;vfv) fv=fv-wi+ci; printf(max=%dn,fm); / fm為最優(yōu)解 return 0;一個(gè)簡單有效的優(yōu)化 完全背包問題有一個(gè)很簡單有效的優(yōu)化,是這樣的:若兩
15、件物品i、j滿足wi=cj,則將物品j去掉,不用考慮。這個(gè)優(yōu)化的正確性顯然:任何情況下都可將價(jià)值小費(fèi)用高的j換成物美價(jià)廉的i,得到至少不會(huì)更差的方案。對(duì)于隨機(jī)生成的數(shù)據(jù),這個(gè)方法往往會(huì)大大減少物品的件數(shù),從而加快速度。然而這個(gè)并不能改善最壞情況的復(fù)雜度,因?yàn)橛锌赡芴貏e設(shè)計(jì)的數(shù)據(jù)可以一件物品也去不掉。轉(zhuǎn)化為01背包問題求解 既然01背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉(zhuǎn)化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選V/wi件,于是可以把第i種物品轉(zhuǎn)化為V/wi件費(fèi)用及價(jià)值均不變的物品,然后求解這個(gè)01背包問題。這樣完全沒有改進(jìn)基本思路的時(shí)間復(fù)雜度,但這畢竟給了
16、我們將完全背包問題轉(zhuǎn)化為01背包問題的思路:將一種物品拆成多件物品。 更高效的轉(zhuǎn)化方法是:把第i種物品拆成費(fèi)用為wi*2k、價(jià)值為ci*2k的若干件物品,其中k滿足wi*2kV。這是二進(jìn)制的思想,因?yàn)椴还茏顑?yōu)策略選幾件第i種物品,總可以表示成若干個(gè)2k件物品的和。這樣把每種物品拆成O(log(V/wi)+1)件物品,是一個(gè)很大的改進(jìn)??偨Y(jié) 完全背包問題也是一個(gè)相當(dāng)基礎(chǔ)的背包問題,它有兩個(gè)狀態(tài)轉(zhuǎn)移方程,分別在“基本思路”以及“O(VN)的算法“的小節(jié)中給出。希望你能夠?qū)@兩個(gè)狀態(tài)轉(zhuǎn)移方程都仔細(xì)地體會(huì),不僅記住,也要弄明白它們是怎么得出來的,最好能夠自己想一種得到這些方程的方法。事實(shí)上,對(duì)每一道動(dòng)
17、態(tài)規(guī)劃題目都思考其方程的意義以及如何得來,是加深對(duì)動(dòng)態(tài)規(guī)劃的理解、提高動(dòng)態(tài)規(guī)劃功力的好方法。三、多重背包問題有N種物品和一個(gè)容量為V的背包。第i種物品最多有ni件可用,每件費(fèi)用是wi,價(jià)值是ci。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大。 基本算法: 這題目和完全背包問題很類似?;镜姆匠讨恍鑼⑼耆嘲鼏栴}的方程略微一改即可,因?yàn)閷?duì)于第i種物品有ni+1種策略:取0件,取1件取ni件。令fiv表示前i種物品恰放入一個(gè)容量為v的背包的最大權(quán)值,則:fiv=maxfi-1v-k*wi+ k*ci|0=k0的最大整數(shù)(注意:這些系數(shù)已經(jīng)可以組合出1ni內(nèi)的所有數(shù)字)
18、。例如,如果ni為13,就將這種物品分成系數(shù)分別為1,2,4,6的四件物品。 分成的這幾件物品的系數(shù)和為ni,表明不可能取多于ni件的第i種物品。另外這種方法也能保證對(duì)于0.ni間的每一個(gè)整數(shù),均可以用若干個(gè)系數(shù)的和表示,這個(gè)證明可以分0.2k-1和2k.ni兩段來分別討論得出,并不難,希望你自己思考嘗試一下。 這樣就將第i種物品分成了O(logni)種物品,將原問題轉(zhuǎn)化為了復(fù)雜度為O(V*logni)的01背包問題,是很大的改進(jìn)。【例3】慶功會(huì)【問題描述】 為了慶賀班級(jí)在校運(yùn)動(dòng)會(huì)上取得全校第一名成績,班主任決定開一場慶功會(huì),為此撥款購買獎(jiǎng)品犒勞運(yùn)動(dòng)員。期望撥款金額能購買最大價(jià)值的獎(jiǎng)品,可以補(bǔ)
19、充他們的精力和體力?!据斎敫袷健?第一行二個(gè)數(shù)n(n=500),m(m=6000),其中n代表希望購買的獎(jiǎng)品的種數(shù),m表示撥款金額。 接下來n行,每行3個(gè)數(shù),v、w、s,分別表示第I種獎(jiǎng)品的價(jià)格、價(jià)值(價(jià)格與價(jià)值是不同的概念)和購買的數(shù)量(買0件到s件均可),其中v=100,w=1000,s=10?!据敵龈袷健?第一行:一個(gè)數(shù),表示此次購買能獲得的最大的價(jià)值(注意!不是價(jià)格)。【輸入樣例】 5 1000 80 20 4 40 50 9 30 50 7 40 30 6 20 20 1【輸出樣例】 1040【解法一】樸素算法 【參考程序】#includeusing namespace std;in
20、t v6002, w6002, s6002;int f6002;int n, m;int max(int x,int y) if (x y) return y; else return x; int main() scanf(%d%d,&n,&m); for (int i = 1; i = n; i+) scanf(%d%d%d,&vi,&wi,&si); for (int i = 1; i = 0; j-) for (int k = 0; k = si; k+) if (j-k*vi0) break; fj = max(fj,fj-k*vi+k*wi); printf(%d,fm); ret
21、urn 0; 【解法二】進(jìn)行二進(jìn)制優(yōu)化,轉(zhuǎn)換為01背包【參考程序】#includeint v10001,w10001;int f6001;int n,m,n1;int max(int a,int b) return ab?a:b; /這句話等于:if (ab) return a; else return b;int main() scanf(%d%d,&n,&m); for(int i=1;i=t) v+n1=x*t; /相當(dāng)于n1+; vn1=x*t; wn1=y*t; s-=t; t*=2; v+n1=x*s; wn1=y*s; /把s以2的指數(shù)分堆:1,2,4,2(k-1),s-2k+1
22、 for(int i=1;i=vi;j-) fj=max(fj,fj-vi+wi); printf(%dn,fm); return 0;小結(jié): 這里我們看到了將一個(gè)算法的復(fù)雜度由O(V*ni)改進(jìn)到O(V*logni)的過程,還知道了存在應(yīng)用超出NOIP范圍的知識(shí)的O(VN)算法。希望你特別注意“拆分物品”的思想和方法,自己證明一下它的正確性,并用盡量簡潔的程序來實(shí)現(xiàn)。 四、混合三種背包問題問題 如果將01背包、完全背包、多重背包混合起來。也就是說,有的物品只可以取一次(01背包),有的物品可以取無限次(完全背包),有的物品可以取的次數(shù)有一個(gè)上限(多重背包)。應(yīng)該怎么求解呢? 01背包與完全背
23、包的混合 考慮到在01背包和完全背包中最后給出的偽代碼只有一處不同,故如果只有兩類物品:一類物品只能取一次,另一類物品可以取無限次,那么只需在對(duì)每個(gè)物品應(yīng)用轉(zhuǎn)移方程時(shí),根據(jù)物品的類別選用順序或逆序的循環(huán)即可,復(fù)雜度是O(VN)。偽代碼如下:for i=1.N if 第i件物品是01背包 for v=V.0 fv=maxfv,fv-wi+ci; else if 第i件物品是完全背包 for v=0.V fv=maxfv,fv-wi+ci; 再加上多重背包 如果再加上有的物品最多可以取有限次,那么原則上也可以給出O(VN)的解法:遇到多重背包類型的物品用單調(diào)隊(duì)列解即可。但如果不考慮超過NOIP范圍
24、的算法的話,用多重背包中將每個(gè)這類物品分成O(log ni)個(gè)01背包的物品的方法也已經(jīng)很優(yōu)了。【例4】混合背包【問題描述】 一個(gè)旅行者有一個(gè)最多能用V公斤的背包,現(xiàn)在有n件物品,它們的重量分別是W1,W2,.,Wn,它們的價(jià)值分別為C1,C2,.,Cn。有的物品只可以取一次(01背包),有的物品可以取無限次(完全背包),有的物品可以取的次數(shù)有一個(gè)上限(多重背包)。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大。【輸入格式】 第一行:二個(gè)整數(shù),V(背包容量,V=200),N(物品數(shù)量,N=30); 第2.N+1行:每行三個(gè)整數(shù)Wi,Ci,Pi,前兩個(gè)整數(shù)分別表示每個(gè)
25、物品的重量,價(jià)值,第三個(gè)整數(shù)若為0,則說明此物品可以購買無數(shù)件,若為其他數(shù)字,則為此物品可購買的最多件數(shù)(Pi)。【輸出格式】 僅一行,一個(gè)數(shù),表示最大總價(jià)值?!緲永斎搿縨ix.in 10 3 2 1 0 3 3 1 4 5 4【樣例輸出】mix.out 11【樣例解釋】 選第一件物品1件和第三件物品2件。 【參考程序】#includeusing namespace std;int m, n;int w31, c31, p31;int f201;int max(int x,int y) return xy?x:y; int main() scanf(%d%d,&m,&n); for (int
26、 i = 1; i = n; i+) scanf(%d%d%d,&wi,&ci,&pi); for (int i = 1; i = n; i+) if (pi = 0) /完全背包 for (int j = wi; j = m; j+) fj = max(fj, fj-wi+ci); else for (int j = 1; j = wi; k-) fk = max(fk,fk-wi+ci); printf(%d,fm); return 0; 小結(jié) 有人說,困難的題目都是由簡單的題目疊加而來的。這句話是否是公理暫且存之不論,但它在本講中已經(jīng)得到了充分的體現(xiàn)。本來01背包、完全背包、多重背包都不
27、是什么難題,但將它們簡單地組合起來以后就得到了這樣一道一定能嚇倒不少人的題目。但只要基礎(chǔ)扎實(shí),領(lǐng)會(huì)三種基本背包問題的思想,就可以做到把困難的題目拆分成簡單的題目來解決。五、二維費(fèi)用的背包問題問題二維費(fèi)用的背包問題是指:對(duì)于每件物品,具有兩種不同的費(fèi)用;選擇這件物品必須同時(shí)付出這兩種代價(jià);對(duì)于每種代價(jià)都有一個(gè)可付出的最大值(背包容量)。問怎樣選擇物品可以得到最大的價(jià)值。設(shè)這兩種代價(jià)分別為代價(jià)1和代價(jià)2,第i件物品所需的兩種代價(jià)分別為ai和bi。兩種代價(jià)可付出的最大值(兩種背包容量)分別為V和U。物品的價(jià)值為ci。算法費(fèi)用加了一維,只需狀態(tài)也加一維即可。設(shè)fivu表示前i件物品付出兩種代價(jià)分別為v
28、和u時(shí)可獲得的最大價(jià)值。狀態(tài)轉(zhuǎn)移方程就是:f ivu=maxfi-1vu,fi-1v-aiu-bi+ci。如前述方法,可以只使用二維的數(shù)組:當(dāng)每件物品只可以取一次時(shí)變量v和u采用逆序的循環(huán),當(dāng)物品有如完全背包問題時(shí)采用順序的循環(huán)。當(dāng)物品有如多重背包問題時(shí)拆分物品。物品總個(gè)數(shù)的限制 有時(shí),“二維費(fèi)用”的條件是以這樣一種隱含的方式給出的:最多只能取M件物品。這事實(shí)上相當(dāng)于每件物品多了一種“件數(shù)”的費(fèi)用,每個(gè)物品的件數(shù)費(fèi)用均為1,可以付出的最大件數(shù)費(fèi)用為M。換句話說,設(shè)fvm表示付出費(fèi)用v、最多選m件時(shí)可得到的最大價(jià)值,則根據(jù)物品的類型(01、完全、多重)用不同的方法循環(huán)更新,最后在f0.V0.M范
29、圍內(nèi)尋找答案。另外,如果要求“恰取M件物品”,則在f0.VM范圍內(nèi)尋找答案?!纠?】潛水員【問題描述】 潛水員為了潛水要使用特殊的裝備。他有一個(gè)帶2種氣體的氣缸:一個(gè)為氧氣,一個(gè)為氮?dú)?。讓潛水員下潛的深度需要各種的數(shù)量的氧和氮。潛水員有一定數(shù)量的氣缸。每個(gè)氣缸都有重量和氣體容量。潛水員為了完成他的工作需要特定數(shù)量的氧和氮。他完成工作所需氣缸的總重的最低限度的是多少? 例如:潛水員有5個(gè)氣缸。每行三個(gè)數(shù)字為:氧,氮的(升)量和氣缸的重量: 3 36 120 10 25 129 5 50 250 1 45 130 4 20 119 如果潛水員需要5升的氧和60升的氮?jiǎng)t總重最小為249(1,2或者4
30、,5號(hào)氣缸)。 你的任務(wù)就是計(jì)算潛水員為了完成他的工作需要的氣缸的重量的最低值?!据斎敫袷健?第一行有2整數(shù)m,n(1=m=21,1=n=79)。它們表示氧,氮各自需要的量。 第二行為整數(shù)k(1=n=1000)表示氣缸的個(gè)數(shù)。 此后的k行,每行包括ai,bi,ci(1=ai=21,1=bi=79,1=ci=800)3整數(shù)。這些各自是:第i個(gè)氣缸里的氧和氮的容量及汽缸重量?!据敵龈袷健?僅一行包含一個(gè)整數(shù),為潛水員完成工作所需的氣缸的重量總和的最低值?!緟⒖汲绦颉?include#include /初始化memset要用到using namespace std;int v, u, k;int a
31、1001, b1001, c1001;int f101101;int main() memset(f,127,sizeof(f); /初始化為一個(gè)很大的正整數(shù) f00 = 0; scanf(%d%d%d,&v,&u,&k); for (int i = 1; i = k; i+) scanf(%d%d%d,&ai,&bi,&ci); for (int i = 1; i = 0; j-) for (int l = u; l = 0; l-) int t1 = j+ ai,t2 = l + bi; if (t1 v) t1 = v; /若氮、氧含量超過需求,可直接用需求量代換, if (t2 u)
32、t2 = u; /不影響最優(yōu)解 if (ft1t2 fjl + ci) ft1t2 = fjl + ci; printf(%d,fvu); return 0; 小結(jié) 事實(shí)上,當(dāng)發(fā)現(xiàn)由熟悉的動(dòng)態(tài)規(guī)劃題目變形得來的題目時(shí),在原來的狀態(tài)中加一維以滿足新的限制是一種比較通用的方法。希望你能從本講中初步體會(huì)到這種方法。 六、分組的背包問題問題有N件物品和一個(gè)容量為V的背包。第i件物品的費(fèi)用是wi,價(jià)值是ci。這些物品被劃分為若干組,每組中的物品互相沖突,最多選一件。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大。算法這個(gè)問題變成了每組物品有若干種策略:是選擇本組的某一件,還是
33、一件都不選。也就是說設(shè)fkv表示前k組物品花費(fèi)費(fèi)用v能取得的最大權(quán)值,則有fkv=maxfk-1v,fk-1v-wi+ci|物品i屬于第k組。使用一維數(shù)組的偽代碼如下:for 所有的組k for v=V.0 for 所有的i屬于組k fv=maxfv,fv-wi+ci注意這里的三層循環(huán)的順序,“for v=V.0”這一層循環(huán)必須在“for 所有的i屬于組k”之外。這樣才能保證每一組內(nèi)的物品最多只有一個(gè)會(huì)被添加到背包中。另外,顯然可以對(duì)每組中的物品應(yīng)用完全背包中“一個(gè)簡單有效的優(yōu)化”?!纠?】分組背包【問題描述】 一個(gè)旅行者有一個(gè)最多能用V公斤的背包,現(xiàn)在有n件物品,它們的重量分別是W1,W2,
34、.,Wn,它們的價(jià)值分別為C1,C2,.,Cn。這些物品被劃分為若干組,每組中的物品互相沖突,最多選一件。求解將哪些物品裝入背包可使這些物品的費(fèi)用總和不超過背包容量,且價(jià)值總和最大?!据斎敫袷健?第一行:三個(gè)整數(shù),V(背包容量,V=200),N(物品數(shù)量,N=30)和T(最大組號(hào),T=10); 第2.N+1行:每行三個(gè)整數(shù)Wi,Ci,P,表示每個(gè)物品的重量,價(jià)值,所屬組號(hào)?!据敵龈袷健?僅一行,一個(gè)數(shù),表示最大總價(jià)值?!緲永斎搿縢roup.in 10 6 3 2 1 1 3 3 1 4 8 2 6 9 2 2 8 3 3 9 3【樣例輸出】group.out 20【參考程序】#include
35、using namespace std;int v, n, t;int w31, c31;int a1132, f201;int main() scanf(%d%d%d,&v,&n,&t); for (int i = 1; i = n; i+) int p; scanf(%d%d%d,&wi,&ci,&p); ap+ap0 = i; for (int k = 1; k = 0; j-) for (int i = 1; i = waki) int tmp = aki; if (fj fj-wtmp+ctmp) fj = fj-wtmp+ctmp; printf(%d,fv); return 0;
36、小結(jié) 分組的背包問題將彼此互斥的若干物品稱為一個(gè)組,這建立了一個(gè)很好的模型。不少背包問題的變形都可以轉(zhuǎn)化為分組的背包問題。 七、有依賴的背包問題簡化的問題 這種背包問題的物品間存在某種“依賴”的關(guān)系。也就是說,i依賴于j,表示若選物品i,則必須選物品j。為了簡化起見,我們先設(shè)沒有某個(gè)物品既依賴于別的物品,又被別的物品所依賴;另外,沒有某件物品同時(shí)依賴多件物品。 算法 這個(gè)問題由NOIP2006金明的預(yù)算方案一題擴(kuò)展而來。遵從該題的提法,將不依賴于別的物品的物品稱為“主件”,依賴于某主件的物品稱為“附件”。由這個(gè)問題的簡化條件可知所有的物品由若干主件和依賴于每個(gè)主件的一個(gè)附件集合組成。 按照背包
37、問題的一般思路,僅考慮一個(gè)主件和它的附件集合。可是,可用的策略非常多,包括:一個(gè)也不選,僅選擇主件,選擇主件后再選擇一個(gè)附件,選擇主件后再選擇兩個(gè)附件無法用狀態(tài)轉(zhuǎn)移方程來表示如此多的策略。(事實(shí)上,設(shè)有n個(gè)附件,則策略有2n+1個(gè),為指數(shù)級(jí)。) 考慮到所有這些策略都是互斥的(也就是說,你只能選擇一種策略),所以一個(gè)主件和它的附件集合實(shí)際上對(duì)應(yīng)于分組的背包中的一個(gè)物品組,每個(gè)選擇了主件又選擇了若干個(gè)附件的策略對(duì)應(yīng)于這個(gè)物品組中的一個(gè)物品,其費(fèi)用和價(jià)值都是這個(gè)策略中的物品的值的和。但僅僅是這一步轉(zhuǎn)化并不能給出一個(gè)好的算法,因?yàn)槲锲方M中的物品還是像原問題的策略一樣多。 再考慮分組的背包中的一句話:
38、可以對(duì)每組中的物品應(yīng)用完全背包中“一個(gè)簡單有效的優(yōu)化”。這提示我們,對(duì)于一個(gè)物品組中的物品,所有費(fèi)用相同的物品只留一個(gè)價(jià)值最大的,不影響結(jié)果。所以,我們可以對(duì)主件i的“附件集合”先進(jìn)行 一次01背包,得到費(fèi)用依次為0.V-wi所有這些值時(shí)相應(yīng)的最大價(jià)值f0.V-wi。那么這個(gè)主件及它的附件集合相當(dāng)于V-wi+1個(gè)物品的物品組,其中費(fèi)用為wi+k的物品的價(jià)值為fk+ci。也就是說原來指數(shù)級(jí)的策略中有很多策略都是冗余的,通過一次01背包后,將主件i轉(zhuǎn)化為 V-wi+1個(gè)物品的物品組,就可以直接應(yīng)用分組的背包的算法解決問題了。 更一般的問題是:依賴關(guān)系以圖論中“森林”的形式給出(森林即多叉樹的集合)
39、,也就是說,主件的附件仍然可以具有自己的附件集合,限制只是每個(gè)物品最多只依賴于一個(gè)物品(只有一個(gè)主件)且不出現(xiàn)循環(huán)依賴。 解決這個(gè)問題仍然可以用將每個(gè)主件及其附件集合轉(zhuǎn)化為物品組的方式。唯一不同的是,由于附件可能還有附件,就不能將每個(gè)附件都看作一個(gè)一般的01 背包中的物品了。若這個(gè)附件也有附件集合,則它必定要被先轉(zhuǎn)化為物品組,然后用分組的背包問題解出主件及其附件集合所對(duì)應(yīng)的附件組中各個(gè)費(fèi)用的附件所對(duì)應(yīng)的價(jià)值。 事實(shí)上,這是一種樹形DP,其特點(diǎn)是每個(gè)父節(jié)點(diǎn)都需要對(duì)它的各個(gè)兒子的屬性進(jìn)行一次DP以求得自己的相關(guān)屬性。這已經(jīng)觸及到了“泛化物品”的思想??赐旰?,你會(huì)發(fā)現(xiàn)這個(gè)“依賴關(guān)系樹”每一個(gè)子樹都等價(jià)于一件泛化物品,求某節(jié)點(diǎn)為根的子樹對(duì)應(yīng)的泛化物品相當(dāng)于求其所有兒子的對(duì)應(yīng)的泛化物品之和。 小結(jié) NOIP2006的那道背包問題,通過引入“物品組”和“依賴”的概念可以加深對(duì)這題的理解,還可以解決它的推廣問題。用物品組的思想考慮那題中極其特殊的依賴關(guān)系:物品不能既作
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 中石化買賣石油合同范本
- 書刊供貨合同范本
- 廠房 設(shè)備維修合同范本
- 網(wǎng)上調(diào)查課題申報(bào)書
- 合同范本組成
- 保潔小區(qū)開荒合同范本
- 醫(yī)用銷售合同范本
- 員工借調(diào)合同范例
- 產(chǎn)品模特簽約合同范本
- 南寧雅閣購車合同范本
- AMDAR資料的分析和應(yīng)用
- 橋梁缺陷與預(yù)防
- 新蘇教版小學(xué)科學(xué)三年級(jí)下冊全冊教案(2022年春修訂)
- 弗洛姆異化理論
- AQL抽樣標(biāo)準(zhǔn)表xls2
- 碳納米管_ppt課件
- 【課件】第2課如何鑒賞美術(shù)作品課件-高中美術(shù)人教版(2019)美術(shù)鑒賞
- 人力資源部經(jīng)理崗位說明書
- [康熙字典9畫五行屬金的字加解釋] 康熙字典五行屬金的字
- 液化氣罐定期檢驗(yàn)方案
- 關(guān)于老年癡呆癥及其智能陪護(hù)設(shè)備的調(diào)查報(bào)告
評(píng)論
0/150
提交評(píng)論