0/1揹包問題
本篇是用回溯法求解0/1揹包問題,結合上篇回溯法求解的步驟(忘了的小夥伴可以再看下),我們來對這個問題進行分析,
解決思路:
(1)確定問題的解題空間樹:從n個集合中求取最優解,很顯然其解空間是子集樹(每個物品要麼裝入,要麼不裝入)。每個結點表示揹包的一種選擇狀態。
(2)確定結點的擴充套件規則:對於本問題的解空間樹,用i表示層數,第i層上的某分枝結點的對應狀態dfs(i,tw,tv,rw,op),tw當前揹包中物品重量,tv當前揹包中物品價值,rw(剪枝函式時用到)考慮第i個物品時剩餘物品的重量,op解向量。拓展可能有兩種:
(1)選擇第i個物品放入揹包,tw=tw+w[i],tv=tv+v[i],(這裡先不講rw的用法,可以先不管),op[i]=1,接著轉向下乙個狀態dfs(i+1,tw,tv,rw,op),這是左分枝。
(2)不選擇第i個物品,op[i]=0,tw=tw,tv=tv,轉向下乙個狀態dfs(i+1,tw,tv,rw,op),這是右分枝。
(1)選擇物品1:op[1]=1,tw=0+5=5,tv=0+4=4,產生新的結點(5,4)作為根的左孩子。
(2)不選擇物品1:op[1]=0,tw=0,tv=0,產生新的結點(0,0),作為根的右孩子。
w陣列是存放物品的重量,v陣列是存放物品的價值,w揹包容量
最後從葉子結點中找出最優解,問題就到此解決,下面上案例和**,我進一步講解:
題目:有4個重量分別為5,3,2,1的物品(物品編號為1~4),對應價值是4,4,3,1給定乙個容量為w=6的揹包。重量用wi表示,價值用vi表示。設計從這些物品中選取部分物品放入揹包,使其具有最大價值。
思維導圖:
圖中畫(x)這個符號,意思是接下來的結點不拓展成為死結點了(剪枝函式的處理結果)
**如下,我用c++/c寫的:
#include#define maxn 20 //最多物種數
int n=4; //4種物品
int w=6; //揹包容量
int ww=0; //揹包最後總重量
int w=; //物品重量 ,不用下標為0的元素
int v=; //物品價值 ,不用下標為0的元素
int x[maxn]; //最終解
int maxv; //最優解的總價值
void dfs(int i,int tw,int tv,int rw,int op)
} }else //尚未找完所有物品
op[i]=0; //不選取第i個物品,回溯
if(tw+rw>w) //右孩子結點剪枝
}}void dispasolution()
} printf("總重量=%d,總價值=%d\n",ww,maxv);
}int main()
執行結果:
最佳裝填方案是:
選取第2個物品
選取第3個物品
選取第4個物品
總重量=6,總價值=8
--------------------------------
process exited after 0.2649 seconds with return value 0
請按任意鍵繼續. .
這裡我們思考下,**是否把所有情況都考慮進去,經過測試,有一種情況執行結果跟實際不符,情況如下:假如揹包未裝滿但此時的揹包價值是最優解(及最大),比揹包裝滿時的價值還大,下面我根據這個例子對**進行修改,這個例子在下面的**中。
#include#define maxn 20 //最多物種數
int n=3; //3種物品
int w=10; //揹包容量
int ww=0; //揹包最後總重量
int w=; //物品重量 ,不用下標為0的元素
int v=; //物品價值 ,不用下標為0的元素
int x[maxn]; //最終解
int maxv; //最優解的總價值
void dfs(int i,int tw,int tv,int rw,int op)
} }else //尚未找完所有物品
op[i]=0; //不選取第i個物品,回溯
if(tw+rw>w) //右孩子結點剪枝
}}void dispasolution()
} printf("總重量=%d,總價值=%d\n",ww,maxv);
}int main()
執行結果如下:
最佳裝填方案是:
選取第2個物品
選取第3個物品
總重量=9,總價值=10
--------------------------------
process exited after 0.5819 seconds with return value 0
請按任意鍵繼續. . .
這樣問題就得以解決了,這個的思維導圖我比較懶呃呃,大家自己畫吧,我給的例子就三個資料,4行就畫完了很快的。
最後說下,時間複雜度的分析:
在進行演算法分析時,語句總的執行次數t(n)是關於問題規模n的函式,進而分析t(n)隨n的變化情況並確定t(n)的數量級。演算法的時間複雜度,也就是演算法的時間量度,記作:t(n}=0(f(n))。它表示隨問題規模n的增大,演算法執行時間的埔長率和 f(n)的埔長率相同,稱作演算法的漸近時間複雜度,簡稱為時間複雜度。其中f( n)是問題規橫n的某個函式。
這樣用大寫o()來體現演算法時間複雜度的記法,我們稱之為大o記法。
如何推導大o階:
用常數1取代執行時間中的所有加法常數。
在修改後的執行次數函式中,只保留最髙階項。
如果最高端項存在且不是1,則去除與這個項相乘的常數。
用這個方法可以得出用回溯法的時間複雜度是o(n)。這裡作為了解,我看了一篇文章是具體些時間複雜度的,好了,有不懂的小夥伴可以問我,與君共勉!!
回溯法 0 1揹包問題
0 1揹包問題 給定n種物品和一揹包.物品i的重量是wi,其價值為ui,揹包的容量為c.問如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大?分析 0 1揹包是子集合選取問題,一般情況下0 1揹包是個np問題.第一步 確定解空間 裝入哪幾種物品 第二步 確定易於搜尋的解空間結構 可以用陣列p,w...
0 1揹包問題 回溯法
0 1揹包問題 回溯法 一 專案描述 每種物品只有2 種選擇,分別為 裝入揹包或不裝入揹包,物品數和揹包容量已給定,計算裝入揹包物品的最大價值和最優裝入方案,用回溯法搜尋子集樹的演算法進行求解。二 演算法設計 a.物品有n種,揹包容量為c,分別用p i 和w i 儲存第i種物品的價值和重量,用x i...
回溯法 0 1揹包問題
include include using namespace std class knap void knap backtrack int i 對第i個物品進行操作 return 如果沒有到葉子節點,就要對這個節點進行操作,即搜尋它的子樹,進入做左子樹表示可以選第i個,進入右子樹表示不能選第i個 ...