分別用蠻力法、動態規劃法、回溯法和分支限界法求解0/1揹包問題。
注:0/1揹包問題:給定種物品和乙個容量為的揹包,物品的重量是,其價值為,揹包問題是如何使選擇裝入揹包內的物品,使得裝入揹包中的物品的總價值最大。其中,每種物品只有全部裝入揹包或不裝入揹包兩種選擇。
1)基本思想:
對於有n種可選物品的0/1揹包問題,其解空間由長度為n的0-1向量組成,可用子集數表示。在搜尋解空間樹時,深度優先遍歷,搜尋每乙個結點,無論是否可能產生最優解,都遍歷至葉子結點,記錄每次得到的裝入總價值,然後記錄遍歷過的最大價值。
2)**:
#include #includeusing namespace std;1)基本思想:#define n 100
struct goods;
int n,bestvalue,cv,cw,c;//物品數量,價值最大,當前價值,當前重量,揹包容量
int x[n],cx[n];//最終儲存狀態,當前儲存狀態
struct goods goods[n];
int force(int i);
int n,bestvalue,cv,cw,c;//物品數量,價值最大,當前價值,當前重量,揹包容量
int x[n],cx[n];//最終儲存狀態,當前儲存狀態
struct goods goods[n];
int knapsack(int n,struct goods a,int c,int x)
else
x[i-1] = 0;
}return v[n][c];
}int main()
int sum2 = knapsack(n,goods,c,x);
printf("動態規劃法求解0/1揹包問題:\nx=[");
for(int i = 0; i < n; i++)
cout<
3)複雜度分析:
動態規劃法求解0/1揹包問題的時間複雜度為:n*c
1)基本思想:
回溯法:為了避免生成那些不可能產生最佳解的問題狀態,要不斷地利用限界函式(bounding function)來處死那些實際上不可能產生所需解的活結點,以減少問題的計算量。這種具有限界函式的深度優先生成法稱為回溯法。
對於有n種可選物品的0/1揹包問題,其解空間由長度為n的0-1向量組成,可用子集數表示。在搜尋解空間樹時,只要其左兒子結點是乙個可行結點,搜尋就進入左子樹。當右子樹中有可能包含最優解時就進入右子樹搜尋。
int n,bestvalue,cv,cw,c;//物品數量,價值最大,當前價值,當前重量,揹包容量
int x[n],cx[n];//最終儲存狀態,當前儲存狀態
struct goods goods[n];
int backtrack(int i)
return bestvalue;
}if(cw + goods[i].wight <= c)
cx[i] = 0;//不裝入揹包
backtrack(i+1);
return bestvalue;
}bool m(struct goods a, struct goods b)
int knapsack3(int n, struct goods a, int c,int x[n])
int main()
int sum3 = knapsack3(n,goods,c,x);
printf("回溯法求解0/1揹包問題:\nx=[");
for(int i = 0; i < n; i++)
cout << x[i] <
printf("] 裝入總價值%d\n",sum3);
return 0;
}
分支限界法類似於回溯法,也是在問題的解空間上搜尋問題解的演算法。一般情況下,分支限界法與回溯法的求解目標不同。回溯法的求解目標是找出解空間中滿足約束條件的所有解,而分支限界法的求解目標則是找出滿足約束條件的乙個解,或是在滿足約束條件的解中找出使某一目標函式值達到極大或極小的解,即在某種意義下的最優解。
首先,要對輸入資料進行預處理,將各物品依其單位重量價值從大到小進行排列。
在下面描述的優先佇列分支限界法中,節點的優先順序由已裝袋的物品價值加上剩下的最大單位重量價值的物品裝滿剩餘容量的價值和。
演算法首先檢查當前擴充套件結點的左兒子結點的可行性。如果該左兒子結點是可行結點,則將它加入到子集樹和活結點優先佇列中。當前擴充套件結點的右兒子結點一定是可行結點,僅當右兒子結點滿足上界約束時才將它加入子集樹和活結點優先佇列。當擴充套件到葉節點時為問題的最優值。
2)**:
#include#includeusing namespace std;#define n 100 //最多可能物體數
struct goods //物品結構體
a[n];
bool m(goods a,goods b)
int max(int a,int b)
else
i = i/2;}}
}//堆中元素下移
void mov_down(heap h, int n, int i)
if(h[i/2].b=x.b)else
}}//獲得堆頂元素並刪除
heap del_top(heap h, int&n)
//計算分支節點的上界
void bound( knapnode* node,int m, goods a, int n)
elseelse
}}//用分支限界法實現0/1揹包問題
int knapsack4(int n,goodsa,int c, int x)
xnode->k = xnode->w = xnode->p = 0;
while(xnode->ks1[ynode->k] = true; // 裝入第k個物體
ynode->w += a[ynode->k].w; // 揹包中物體重量累計
ynode->p += a[ynode->k].p; // 揹包中物體價值累計
ynode->k ++; // 搜尋深度++
bound(ynode, c, a, n); // 計算結點y的上界
y.b = ynode->b;
y.p = ynode;
insert(heap, y, k); //結點y按上界的值插入堆中
znode = new knapnode; // 建立結點z
*znode = *xnode; //結點x的資料複製到結點z
znode->k++; // 搜尋深度++
bound(znode, c, a, n); //計算節點z的上界
z.b = znode->b;
z.p = znode;
insert(heap, z, k); //結點z按上界的值插入堆中
delete xnode;
x = del_top(heap, k); //獲得堆頂元素作為新的父親結點
xnode = x.p;
}v = xnode->p;
for( i=0; is1[i])else
}delete xnode;
delete heap;
return v; //返回揹包中物體的價值
}/*測試以上演算法的主函式*/
int main()
{ goods b[n];
printf("物品種數n: ");
scanf("%d",&n); //輸入物品種數
printf("揹包容量c: ");
scanf("%d",&c); //輸入揹包容量
for (int i=0;i
0 1揹包問題的四種方法
0 1揹包問題 給定n個物品和乙個容量為c的揹包,物品i的重量是wi,其價值是vi,0 1揹包問題要求從這n個物品中,選擇裝入揹包的最優組合 物品不可以分割 使得裝入揹包中的物品的總價值最大。例如,將8件物品放入容量為15的揹包,8件物品的重量和價值分別為 w v 對0 1揹包問題,可以設計多種貪心...
01揹包四種方式實現
暴力遞迴 暴力列舉 param i 輸入規模 param capacity 揹包容量 return 揹包最大價值 public intforceknapsacksr int i,int capacity 每一件商品都被選擇完了 else if i 1 int p1 forceknapsacksr i...
關於RMQ問題的四種解法
什麼是rmq問題 rmq range minimum maximum query 對於長度為n的陣列a,回答若干詢問rmq a,i,j i,j n 1 返回陣列a中下標在i,j範圍內的最小 大 值,也就是說,rmq問題是指求區間最值的問題。1.暴力法最簡單的方法,就是遍歷陣列直接搜尋,但是這種方式時...