日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

背包问题的应用

發布時間:2024/7/19 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 背包问题的应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?http://acm.hdu.edu.cn/showproblem.php?pid=2602??Bone Collector

Problem Description

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

Output

One integer per line representing the maximum of the total value (this number will be less than 231).

Sample Input

1

5 10

1 2 3 4 5

5 4 3 2 1

Sample Output

14

這是一個最簡單的01背包問題。

#include <iostream> using namespace std;const int MAX = 10010; int f[MAX]; int V; //背包的體積void ZeroOnePack (int cost, int weight) {int v;for (v = V; v >= cost; v--)f[v] = f[v] > (f[v - cost] + weight) ? f[v] : (f[v - cost] + weight); }int value[1005],volume[1005];int main(void) {int t,num,i;scanf("%d",&t);while(t--){scanf("%d %d",&num,&V);for(i=0;i<=V;i++) //沒有要求把背包裝滿{f[i]=0;}for(i=1;i<=num;i++)scanf("%d",&value[i]);for(i=1;i<=num;i++)scanf("%d",&volume[i]);for(i=1;i<=num;i++)ZeroOnePack(volume[i], value[i]);printf("%d\n",f[V]);}return 0; }

http://acm.zjut.edu.cn/ShowProblem.aspx?ShowID=1355??擎天柱
?Description:
話說月光家里有許多玩具,最近他又看上了DK新買的“擎天柱”,就想用自己的跟DK的換。每種玩具都有特定的價格,價格為整數。只有月光拿出的玩具的總價格與“擎天柱”的價格相等才能換得“擎天柱”。同時,月光還希望能用最少的玩具數換回“擎天柱”。請問,月光能順利得到夢寐以求的“擎天柱”嗎?

Input:
輸入數據包含多組;對于每組數據,第一行為一個正整數n(1 ≤n≤10); 表示月光手頭有n個玩具。接下來一行有n個正整數P1,P2,……,Pn(1 ≤ Pi ≤ 1000),Pi為第i個玩具的所對應的價格。最后一行為一個正整數m(1 ≤ m ≤10000),為“擎天柱”的價格。

Output:
對于每組數據,如果能換得“擎天柱”則輸出最少玩具數;否則,輸出“-1”。

Sample Input:
3
1 2 3
4
4
4 3 3 5
2
Sample Output:
2
-1
這是浙工大ACM上的題目做完可以提交試試。。

想用剛才的方法做?發現題目里多了什么條件沒?

?????? 對了!那就是“只有月光拿出的玩具的總價格與“擎天柱”的價格相等才能換得“擎天柱”這句。 換句話說題目要求的不僅僅是最優值而且要求你求的是“能把包裝滿”的最優值!!!

?????? 怎么辦?難道就這樣束手無策了么?

解答:

0-1背包也是背包問題的最常見的兩種問法:

一是要求“恰好裝滿背包”時的最優解

二是“沒有要求必須把背包裝滿”。

這兩種問法的實現方法不同點主要在初始化上。

如果是第一種問法,要求恰好裝滿背包,那么在初始化時除了f[0]為0其它f[1..V]均設為-∞,這樣就可以保證最終得到的f[N]是一種恰好裝滿背包的最優解。

如果并沒有要求必須把背包裝滿,而是只希望價格盡量大,初始化時應該將f[0..V]全部設為0。
為什么呢?

可以這樣理解:初始化的f數組事實上就是在沒有任何物品可以放入背包時的合法狀態。如果要求背包恰好裝滿,那么此時只有容量為0的背包可能被價值為0的nothing“恰好裝滿”,其它容量的背包均沒有合法的解,屬于未定義的狀態,它們的值就都應該是-∞了。如果背包并非必須被裝滿,那么任何容量的背包都有一個合法解“什么都不裝”,這個解的價值為0,所以初始時狀態的值也就全部為0了。

這個小技巧完全可以推廣到其它類型的背包問題,后面也就不再對進行狀態轉移之前的初始化進行講解。

#include <stdio.h> #include <stdlib.h>const int MAX = 10010; int f[MAX]; int V; //背包的體積void ZeroOnePack (int cost, int weight) {int v;for (v = V; v >= cost; v--)f[v] = f[v] < (f[v - cost] + weight) ? f[v] : (f[v - cost] + weight); //這里取較小的那個值 }int value[15];int main(void) {int num,i;while(scanf("%d",&num)==1){for(i=1;i<=num;i++)scanf("%d",&value[i]);scanf("%d",&V);f[0]=0;for(i=1;i<=V;i++) //要求把背包裝滿{f[i]=0x3f; //初始化為一個比較大的值}for(i=1;i<=num;i++)ZeroOnePack(value[i],1);if(f[V]!=0x3f)printf("%d\n",f[V]);elseprintf("-1\n");}return 0; }


完全背包的應用:

有了對0-1背包的深刻認識,我們終于可以看看它的幾種變形了。首先登場的是完全背包,何為“完全背包”,就是每樣東西都有無窮多個。

我們還是先來到題目看看究竟吧!~

http://acm.hdu.edu.cn/showproblem.php?pid=1248? 寒冰王座

Problem Description

不死族的巫妖王發工資拉,死亡騎士拿到一張N元的鈔票(記住,只有一張鈔票),為了防止自己在戰斗中頻繁的死掉,他決定給自己買一些道具,于是他來到了地精商店前.

死亡騎士:"我要買道具!"

地精商人:"我們這里有三種道具,血瓶150塊一個,魔法藥200塊一個,無敵藥水350塊一個."

死亡騎士:"好的,給我一個血瓶."

說完他掏出那張N元的大鈔遞給地精商人.

地精商人:"我忘了提醒你了,我們這里沒有找客人錢的習慣的,多的錢我們都當小費收了的,嘿嘿."

死亡騎士:"......"

死亡騎士想,與其把錢當小費送個他還不如自己多買一點道具,反正以后都要買的,早點買了放在家里也好,但是要盡量少讓他賺小費.

現在死亡騎士希望你能幫他計算一下,最少他要給地精商人多少小費。

Input

輸入數據的第一行是一個整數T(1<=T<=100),代表測試數據的數量.然后是T行測試數據,每個測試數據只包含一個正整數N(1<=N<=10000),N代表死亡騎士手中鈔票的面值.

注意:地精商店只有題中描述的三種道具。

Output

對于每組測試數據,請你輸出死亡騎士最少要浪費多少錢給地精商人作為小費。

Sample Input

2

900

250

Sample Output

0

50

關鍵:

轉化為01背包問題求解
解題思路:

?????? 既然01背包問題是最基本的背包問題,那么我們可以考慮把完全背包問題轉化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選V/c[i]件,于是可以把第i種物品轉化為V/c[i]件費用及價值均不變的物品,然后求解這個01背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將完全背包問題轉化為01背包問題的思路:將一種物品拆成多件物品。

?????? 但我們有更優的O(VN)的算法。這個算法使用一維數組,先看偽代碼:

??? for i=1..N

??????? for v=0..V

????????????? f[v]=max{f[v],f[v-c[i]]+w[i]};

?????? 你會發現,這個偽代碼與0-1背包的偽代碼只有v的循環次序不同而已。為什么這樣一改就可行呢?首先想想為什么0-1背包中要按照v=V..0的逆序來循環。這是因為要保證第i次循環中的狀態f[i][v]是由狀態f[i-1][v-c[i]]遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮“選入第i件物品”這件策略時,依據的是一個絕無已經選入第i件物品的子結果f[i-1][v-c[i]]。而現在完全背包的特點恰是每種物品可選無限件,所以在考慮“加選一件第i種物品”這種策略時,卻正需要一個可能已選入第i種物品的子結果f[i][v-c[i]],所以就可以并且必須采用v=0..V的順序循環。這就是這個簡單的程序為何成立的道理。

?????? 這個算法也可以以另外的思路得出。例如,基本思路中的狀態轉移方程可以等價地變形成這種形式:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},將這個方程用一維數組實現,便得到了上面的偽代碼。

下面是AC的代碼:

#include <stdio.h> #include <stdlib.h>const int MAX = 10010; int f[MAX]; //MAX要比背包的體積大 int V; //背包的體積void CompletePack (int cost, int weight) //完全背包 {int v;for (v = cost; v <= V; v++)f[v] = f[v] > (f[v - cost] + weight) ? f[v] : (f[v - cost] + weight); }int value[4]={0,150,200,350};int main(void) {int t,i;scanf("%d",&t);while(t--){scanf("%d",&V);for(i=0;i<=V;i++) //沒有要求把背包裝滿{f[i]=0;}for(i=1;i<=3;i++)CompletePack(value[i],value[i]);printf("%d\n",V-f[V]);}return 0; }

?多重背包
當每種物品不是1個也不是無窮多個,而是指定的n個時。就出現了我現在所要說的多重背包問題。
基本算法
這題目和完全背包問題很類似。基本的方程只需將完全背包問題的方程略微一改即可,因為對于第i種物品有n[i]+1種策略:取0件,取1件……取n[i]件。令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值,則有狀態轉移方程:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}

復雜度是O(V*Σn[i])。【好慢哦】

轉化為01背包問題

另一種好想好寫的基本方法是轉化為01背包求解:把第i種物品換成n[i]件01背包中的物品,則得到了物品數為Σn[i]的01背包問題,直接求解,復雜度仍然是O(V*Σn[i])。【依然是慢的】

參考思路:

利用二進制的思想我們考慮把第i種物品換成若干件物品,使得原問題中第i種物品可取的每種策略——取0..n[i]件——均能等價于取若干件代換以后的物品。另外,取超過n[i]件的策略必不能出現。

??? 方法是:將第i種物品分成若干件物品,其中每件物品有一個系數,這件物品的費用和價值均是原來的費用和價值乘以這個系數。使這些系數分別為1,2,4,...,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數。例如,如果n[i]為13,就將這種物品分成系數分別為1、2、4、6的四件物品。

分成的這幾件物品的系數和為n[i],表明不可能取多于n[i]件的第i種物品。另外這種方法也能保證對于0..n[i]間的每一個整數,均可以用若干個系數的和表示,這個證明可以分0..2^k-1和2^k..n[i]兩段來分別討論得出,并不難,希望你自己思考嘗試一下。

這樣就將第i種物品分成了O(log n[i])種物品,將原問題轉化為了復雜度為

O(V*Σlog n[i])的01背包問題,是很大的改進。

下面給出O(log amount)時間處理一件多重背包中物品的過程,其中amount表示物品的數量:

procedure MultiplePack(cost,weight,amount)

??? if cost*amount>=V

??????? CompletePack(cost,weight)

??????? return

??? integer k=1

??? while k<amount

??????? ZeroOnePack(k*cost,k*weight)

??????? amount=amount-k

??????? k=k*2

??? ZeroOnePack(amount*cost,amount*weight)

希望你仔細體會這個偽代碼,如果不太理解的話,看看下面我AC的程序,單步執行幾次,或者頭腦加紙筆模擬一下,也許就會慢慢理解了。
?http://acm.hdu.edu.cn/showproblem.php?pid=2191

Problem Description
急!災區的食物依然短缺!
為了挽救災區同胞的生命,心系災區同胞的你準備自己采購一些糧食支援災區,現在假設你一共有資金n元,而市場有m種大米,每種大米都是袋裝產品,其價格不等,并且只能整袋購買。
請問:你用有限的資金最多能采購多少公斤糧食呢?

后記:
人生是一個充滿了變數的生命過程,天災、人禍、病痛是我們生命歷程中不可預知的威脅。
月有陰晴圓缺,人有旦夕禍福,未來對于我們而言是一個未知數。那么,我們要做的就應該是珍惜現在,感恩生活——
感謝父母,他們給予我們生命,撫養我們成人;
感謝老師,他們授給我們知識,教我們做人
感謝朋友,他們讓我們感受到世界的溫暖;
感謝對手,他們令我們不斷進取、努力。
同樣,我們也要感謝痛苦與艱辛帶給我們的財富~
Input
輸入數據首先包含一個正整數C,表示有C組測試用例,每組測試用例的第一行是兩個整數n和m(1<=n<=100, 1<=m<=100),分別表示經費的金額和大米的種類,然后是m行數據,每行包含3個數p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分別表示每袋的價格、每袋的重量以及對應種類大米的袋數。
Output
對于每組測試數據,請輸出能夠購買大米的最多重量,你可以假設經費買不光所有的大米,并且經費你可以不用完。每個實例的輸出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400

再看看我AC的代碼,幫助你進一步的理解這類問題:

#include <stdio.h> #include <stdlib.h>int V; //背包的體積 const int MAX = 10010; int f[MAX]; //MAX要比背包的體積大 int p[MAX], h[MAX], c[MAX];void ZeroOnePack (int cost, int weight) //01背包 {int v;for (v = V; v >= cost; v--)f[v] = f[v] > (f[v - cost] + weight) ? f[v] : (f[v - cost] + weight); }void CompletePack (int cost, int weight) //完全背包 {int v;for (v = cost; v <= V; v++)f[v] = f[v] > (f[v - cost] + weight) ? f[v] : (f[v - cost] + weight); }void MultiplePack(int cost, int weight, int amount) //多重背包 {if (cost * amount >= V)CompletePack (cost, weight);int k = 1;while (k < amount){ZeroOnePack (k * cost, k * weight);amount = amount - k;k = k * 2;}ZeroOnePack (amount * cost, amount * weight); }int main(void) {int t,i,m;scanf("%d",&t);while(t--){scanf("%d %d",&V,&m);for(i=0;i<=V;i++) //沒有要求把背包裝滿{f[i]=0;}for(i=1;i<=m;i++)scanf("%d %d %d",&p[i],&h[i],&c[i]);for(i=1;i<=m;i++)MultiplePack(p[i], h[i], c[i]);printf("%d\n",f[V]);}return 0; }


?

?

?

總結

以上是生活随笔為你收集整理的背包问题的应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。