1. 程式人生 > >0——1揹包問題(動態規劃)

0——1揹包問題(動態規劃)

1,0-1揹包

問題描述:

給定n個物品和一個揹包,第i個物品的重量為Wi,其價值為Vi.揹包的總容量為c,問如何選取物品使得揹包中所裝入物品價值最大並且裝入揹包物品重量之和不超過揹包總重量。

問題分析:

需要注意的是,再把物品裝入揹包時,每種物品i都有兩種選擇,裝入揹包和不裝入揹包,不可以將物品i多次裝入揹包,也不可以只裝入物品i的一部分。如果我們用Xi來表示物品i,那麼當Xi=0時表示物品i沒有裝入揹包,當Xi!=0時就說物品i裝入揹包,因次0——1揹包問題的一個最優解就是一個n元0-1向量(X1,X2 ......Xn)(X1 ,X2 X3 ......Xn),(Xn\epsilon (0,1))使得\sum_{i=1}^{n}Wi*Xi<=c,而且\sum_{i=1}^{n}Xi*Vi達到最大的問題。因此,我們可以把問題描述為以下約束函式:約束條件:\sum_{i=1}^{n}Wi*Xi<=cXi\epsilon (0,1)0\leq i\leq n),目標函式\sum_{i=1}^{n}Xi*Vi

將上述問題的最優解記錄在m(i,j)中,即m(i,j)是在揹包容量為j,可選物品為i,i+1,.....n時0-1揹包問題的最優值,也就是被放入揹包的所有物品的最大價值總和,最優解就是0-1向量(x_{i},x_{i+1}.....x_{n})

,在這裡,m(i+1,j^{'})表示揹包容量為j^{'}時可選物品為i+1,i+2,....n時0-1揹包問題的最優值,因此m(i+1,j^{'})問題是m(i,j)問題的子問題,要由m(i+1,j^{'})問題得到m(i,j)問題只需要考慮第i個物品是否放入揹包即可,分為兩種情況,如果m(i+1,j^{'})中揹包容量j^{'}不足以放入第i個物品,那麼m(i+1,j)表示m(i,j)問題不放入第i件物品,反之即可以放入第i件物品,這時有兩種選擇,放入,即m(i,j)=m(i+1,j-w_{i})+v_{i},另一種是不放入,m(i,j)=m(i+1,j),只要取兩者中最大者即可,當m(i,j)問題一直分解,直至有一個物品時,m(i,j)問題 就變成了m(n,j)問題,表示可選物品只有第n件時的最優值,因此是否裝入第n件物品只有兩種情況。

根據約束條件建立揹包的遞迴關係:

m(i,j)=\left\{\begin{matrix} m(i+1,j)(0\leq J\leq _W{i})& & & & & & & & & & & & & & & & & & & \\ max(m(i+1,j),m(i+1,j-_W{i})+_V{i})(j\geq _W{i})& & & & & & & & & & & & & & & & & & & \end{matrix}\right.

m(n,j)=\left\{\begin{matrix} _V{n} & & (j\geq _W{n}) & & & & & & & \\ 0& &(0\leq j\leq _W{n}) & & & & & & & \end{matrix}\right.

遞迴求最優解

根據演算法Knapsack我們求解的只是揹包在容量為c,可選物品為1,2,3.......n時揹包所獲得的最大價值。我們可以通過maxValue來構造最優解。從maxValue[n][c]

開始,n代表第n個物品,如果maxValue[n][c]=maxValue[n-1][c],則x_{n}=0,否則x_{n}=1,然後遞推到maxValue[n-1][c-w_{n}],一直進行遞推,直到maxValue[0][j],如果maxValue[0][j]\neq 0,則x_{0}=1,否則x_{0}=0,至此,我們已經構造出最優解。

根據描述,0-1揹包問題中有n件物品,陣列weight存放物品重量,value存放物品價值,二維陣列maxValue存放當揹包容量為j時,可選物品為i,i+1,i+2,...n時0-1揹包問題的最優解。程式碼實現如下:


#include "pch.h"
#include <iostream>
	using namespace std;
	void Knapsack(int *weight, int *value, int W_num, int V_num, int maxWeight)
	{
		int *flag = new int[W_num];//構造最優解的標誌陣列,存放沃品個數
		int **maxValue = new int *[W_num ];//建立動態二維陣列,行代表物品個數,列代表此時的揹包容量
		for (int i = 0; i < W_num; i++)
			maxValue[i] = new int[maxWeight + 1];
		for (int j = 0; j < W_num; j++)//初始化為0
		{
			for (int k = 0; k <=maxWeight; k++)
				maxValue[j][k] = 0;
		}
		for (int i = 0; i < W_num; i++)
			maxValue[i][0] = 0;
		for (int k = 0; k <= maxWeight; k++)//放入第一個物品
		{
			maxValue[0][k] = (k < weight[0]) ? 0 : value[0];
		}
		for (int i = 1; i < W_num; i++)
		{
			for (int j = 0; j <= maxWeight; j++)//代表當前揹包容量
			{
				if (j >= weight[i])
					maxValue[i][j] = (maxValue[i - 1][j] >=maxValue[i - 1][j - weight[i]] + value[i]) ? maxValue[i - 1][j] : (maxValue[i - 1][j - weight[i]] + value[i]);
				else
					maxValue[i][j] = maxValue[i - 1][j];
			}
		}
		
		cout << "揹包能獲得的最大價值是:" << maxValue[W_num - 1][maxWeight ] << endl;
		//構造最優解
		for (int i = W_num-1; i > 0; i--)
		{
			if (maxValue[i][maxWeight] == maxValue[i - 1][maxWeight])
				flag[i] = 0;
			else
			{
				flag[i] = 1;
				maxWeight = maxWeight - weight[i];
			}
		}
		if (maxValue[0][maxWeight])
		{
			flag[0] = 1;
		}
		cout << "裝入揹包的物品依次是:";
		for (int i = 0; i < W_num; i++) {
			if(flag[i]!=0)
			cout <<" 第"<<i+1<<"件";
		}
		for (int i = 0;i < W_num;i++)//釋放記憶體空間
		{
			delete[] maxValue[i];
		}
		delete []maxValue;
                delete []weight;
                delete []Value;
	}
	int main()
	{
		int biggestweight;
		int countNum;
		cout << "請輸入揹包最大容量:" << endl;
		
		cin >> biggestweight;
		cout << "請輸入物品數量:" << endl;
		cin >> countNum;
		int *weight = new int[countNum];
		int *value = new int[biggestweight];
		cout << "請輸入每種物品的重量:" << endl;
		for (int i = 0;i < countNum;i++)
		{
			cin >> weight[i];
		}
		cout << "請輸入每種物品的價值:" << endl;
		for (int i = 0;i < countNum;i++)
			cin >> value[i];
		Knapsack(weight, value, 5, 5, 10);
		

	}

根據演算法中的遞迴關係,演算法Knapsack的O(n*c),而構造最優解的過程需要O(n)的計算時間。

2,揹包問題

揹包問題與0-1揹包問題很相似,約束條件基本一致,但也有其差別,在於揹包問題在裝入物品時可以選擇一部分作為裝入,而0-1揹包對於沒裝物品要不裝入,要不不裝入,所以在探討0-1揹包的基礎上我們來看看揹包問題。顧名思義,揹包問題要求是在給定揹包容量的限定下,要使裝入揹包物品的價值達到最大。在這裡我們有兩種裝法:(1)我們首每次都裝所剩下物品中價值最大的,(2)每次都選擇單位價值最大的物品裝入直至揹包裝滿。對於這兩種裝法,我們都用到貪心策略,對於第一種裝法,我們可以把所給的物品按照從大到小排序,然後依次裝入揹包,直至揹包裝滿。第二種裝法,我們先對每一種物品求出其V_{i}/W_{i}(即單位價格的重量),然後每次裝入我們都選擇單位價格最大的物品裝入,直到揹包裝滿求出其總價值。經過對比我們發現,顯然第二幢方法獲得的價值量要大於第一種方法。所以,對於揹包問題,我們一般用貪心策略每次裝入都選擇單位價值最大的,最後可以獲得最大價值量。

// 揹包問題.cpp : 此檔案包含 "main" 函式。程式執行將在此處開始並結束。
//

#include "pch.h"
#include <iostream>
using namespace std;
struct goods {
	float weight;
	float value;
	float per_price;
};
void Knapsack(goods *good, float biggestWeight, int n)
{
	int *arr = new int[n];
	int *flag = new int[n];
	for (int i = 0;i < n;i++)
	{
		
		good[i].per_price = good[i].value / good[i].weight;//記錄單位價格
	}
	float temp;
	int max;
	for (int i = 0;i < n-1;i++)
	{
		max = i;
		for (int j = i+1;j < n;j++)
		{
			if (good[j].per_price > good[i].per_price)
				max = j;//記錄下標
		}
		if (max != i)
		{
			temp = good[i].per_price;
			good[i].per_price= good[max].per_price;
			good[max].per_price = temp;
			temp = good[i].value;
			good[i].value = good[max].value;
			good[max].value = temp;
			temp = good[i].weight;
			good[i].weight = good[max].weight;
			good[max].weight = temp;

		}
	}
	int i = 0;
	float bigValue = 0;
	for ( i = 0;i < n;i++)
	{
		if (good[i].weight <=biggestWeight)
		{
			bigValue += good[i].value;
			biggestWeight -= good[i].weight;
			flag[i] = 1;
		}
		else {
			bigValue += good[i].per_price*biggestWeight;
			flag[i] = 1;
			break;
		}
		
	}
	cout << "揹包獲得最大價值是:" << bigValue << endl;
	for (int i = 0;i < n;i++)
	{
		cout << good[i].value << " " << good[i].weight << " " << good[i].per_price << endl;
	}
		delete []arr;
		delete[]flag;
}
int main()
{
	//揹包問題
	cout << "請輸入物品個數:" << endl;
	int goodNum;
	cin >> goodNum;
	cout << "請輸入揹包最大容量:" << endl;
	float biggestWeight;
	cin >> biggestWeight;
	goods *good = new goods[goodNum];
	cout << "請輸入每種商品的價值和重量:" << endl;
	for (int i = 0;i < goodNum;i++)
		cin >> good[i].value >> good[i].weight;
	Knapsack(good, biggestWeight, goodNum);
	
   
}

相關推薦

0-1揹包問題-動態規劃

    揹包問題可以使用動態規劃獲得最優解,動態規劃的思路是:通過獲得單階段的最優解後,升級到多階段,每次升級時都使用上一階段的最優解計算,避免遍歷所有可能時產生的時間消耗。 package test; import java.util.Ar

BZOJ5302 HAOI2018奇怪的揹包動態規劃

  由裴蜀定理,子集S有解當且僅當gcd(S,P)|w。   一個顯然的dp是設f[i][j]為前i個數gcd為j的選取方案。注意到這裡的gcd一定是P的約數,所以狀態數是n√P的。然後可以通過這個得到gcd是j約數的選取方案。複雜度O(n√PlogP)。   考慮優化。注意到每個數取gcd後的貢獻僅與其

0-1揹包優化動態規劃演算法之跳躍點法

// 動態規劃 揹包問題 跳躍點優化 #include <iostream> using namespace std; template<class Type> void Traceback(int n,Type w[],Type v[]

0/1揹包問題動態規劃 空間複雜度是o(C)

有num個物品,總揹包容量為Capacity, 求不超過揹包總容量的前提下使得揹包裡的物品的價值達到最大的物品是哪些物品。 對於每個物品,只有兩種選擇,要麼裝要麼不裝進揹包。 那麼在考慮前 i 個物品

0——1揹包問題動態規劃求解

轉自https://www.cnblogs.com/wujing-hubei/p/6376218.html?utm_source=tuicool&utm_medium=referral 這篇文章把動態規劃過程剖析的很好 基本思想: 動態規劃演算法通常用於求解具有某種

0-1揹包問題動態規劃

http://acm.hdu.edu.cn/showproblem.php?pid=1171 這道題咋看有點複雜,其實也只是換了一種思維,因為題目要求要儘量平均分配,所以我們可以先將總價值sum求出,然後得出其分配的平均值為sum/2,要注意這個答案可能為小數,但是又因為sum是整數,所以最

0-1揹包問題簡單實現程式碼動態規劃

import java.util.Scanner; /** * @ClassName Backpack * @Description 0-1揹包問題 * @Author lzq * @Date 2018/12/6 17:51 * @Version 1.0 **/ class

0——1揹包問題動態規劃

1,0-1揹包 問題描述: 給定個物品和一個揹包,第個物品的重量為,其價值為.揹包的總容量為,問如何選取物品使得揹包中所裝入物品價值最大並且裝入揹包物品重量之和不超過揹包總重量。 問題分析: 需要注意的是,再把物品裝入揹包時,每種物品都有兩種選擇,裝入揹包和不裝入揹包

0-1揹包演算法動態規劃

給定n種物品和一揹包。物品i的重量是wi,其價值為vi,揹包的容量為C。問應如何選擇裝入揹包的物品,使得裝入揹包中物品的總價值最大? 0-1揹包問題是一個特殊的整數規劃問題。 設所給0-1揹包問題的子問題 的最優值為m(i,j),即m(i,j)是揹包容量為j,可選擇物品為

【HDU - 2546】飯卡 dp,0-1揹包,貪心思想

電子科大本部食堂的飯卡有一種很詭異的設計,即在購買之前判斷餘額。如果購買一個商品之前,卡上的剩餘金額大於或等於5元,就一定可以購買成功(即使購買後卡上餘額為負),否則無法購買(即使金額足夠)。所以大家都希望儘量使卡上的餘額最少。  某天,食堂中有n種菜出售,每種菜可購買一次。已知每種菜

51Nod1085 0-1揹包一維和二維陣列實現

揹包是典型的動態規劃問題,關於揹包問題的詳解,推薦部落格:點選開啟連結(這篇部落格有點錯誤,程式碼for迴圈裡錯了,不過講解 的很詳細) 題目如下: 在N件物品取出若干件放在容量為W的揹包裡,每件物品的體積為W1,W2……Wn(Wi為整數),與之相對應的價值為P1,P2……Pn(Pi為整數)

0-1揹包 反向、概率HDOJ 2955

Robberies Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 31576    Accepted Submission(s

納新題】湊數題恰好裝滿類0-1揹包 或 母函式

題幹: 描述 小Q手裡有n枚硬幣,每枚硬幣有一定的金額x,他想知道,用這些硬幣能組成多少種不同的金額。但是他太笨了,自己數懵了,你來幫幫他好不好? 注意:組成金額時,每枚硬幣只能用一次,但可以同時使用等面值的不同硬幣 輸入 第一行 n,表示第二行一共有n個數

01揹包問題動態規劃

揹包問題 最近剛學了01揹包問題,但是聽老師講再加上以前自己看書看的,發現有很多地方很容易搞混,原理就是劃分找動態轉移方程,但是寫程式時會遇到困難,趁著今天有空,就特意整理一下01揹包問題。 動態規劃的基本思想: 動態規劃演算法通常用於求解具有某種最優性質的問題

01揹包問題動態規劃python實現

        在01揹包問題中,在選擇是否要把一個物品加到揹包中,必須把該物品加進去的子問題的解與不取該物品的子問題的解進行比較,這種方式形成的問題導致了許多重疊子問題,使用動態規劃來解決。n=5是物品的數量,c=10是書包能承受的重量,w=[2,2,6,5,4]是每個物

揹包問題動態規劃

01揹包問題有n個重量和價值分別為wi,vi的物品,從這些物品中挑選出總重量不插過W的物品,求所有挑選方案中價值總和的最大值限制條件1<=n<=1001<=wi,vi<=1001

0-1揹包回溯法剪枝版

#include<stdio.h> #define n 3 int maxvalue=0,tot=0; int w[n]={16,15,15},v[n]={45,25,25},c=30; int tempweight=0,tempvalue=0; void df

1085] 揹包問題動態規劃

Problem Description 在N件物品取出若干件放在容量為W的揹包裡,每件物品的體積為W1,W2……Wn(Wi為整數),與之相對應的價值為P1,P2……Pn(Pi為整數)。求揹包能夠容納的最大價值。 Input 第1行,2個整數,N和W中間

hud2059龜兔賽跑動態規劃

n+1 動物 include output script text sam 起跑線 other 龜兔賽跑 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)T

ship動態規劃

動態規劃 輸出 一個 子序列 升序 端點 如果 2個 長度 (ships.pas/c/cpp) 來源:《奧賽經典》(提高篇)【問題描述】PALMIA國家被一條河流分成南北兩岸, 南北兩岸上各有N個村莊。 北岸的每一個村莊有一個唯一的朋友在南岸,且他們的朋友村莊彼此不同。每一