一、問題描述
0-1揹包問題可描述爲:n個物體和一個揹包。對物體i,其價值爲value,重量爲weight,揹包的容量爲W。如何選取物品裝入揹包,使揹包中所裝入的物品總價值最大?
二、算法設計
2.1設計算法所需的數據結構。採用結構體Goods來存放單個貨物信息,揹包容量爲nKnapSackCap,自定義數據類型vector<vector<int>>用來存放每一次迭代的執行結果,用數組knapStackGoods存放最終裝入揹包的物品。
2.2初始化。(由於vector類功能的限制,這裏爲了方便將record所有元素設置爲0)。
2.3按照下式遞推record,確定前i個物品能夠裝入揹包的最優值。
2.4求解最優值,結果存入數組knapStackGoods中。
三、算法描述
//問題求解
void KnapSack_0_1(AllGoods &allGoods,int knapSackCap,AllGoods &knapStackGoods)
{
RecordTable record;//定義記錄表
vector<int> temp;//一個臨時變量,用來初始化record
//將record所有元素初始化爲0
for(int i = 0;i <= allGoods.size();++i)
{
record.push_back(temp) ;
for(int j = 0;j <=knapSackCap;++j)
{
record[i].push_back(0);
}
}
//計算record[i][j]
for(int i = 1;i <= allGoods.size();++i)
{
for(int j = 1;j <=knapSackCap;++j)
{
if(j < allGoods[i - 1].weight)
{
record[i][j] = record[i - 1][j];
}
else
{
if(record[i - 1][j - allGoods[i - 1].weight] + allGoods[i -1].value < record[i - 1][j] )
{
record[i][j] = record[i - 1][j];
}
else
{
record[i][j] = record[i - 1][j - allGoods[i - 1].weight] + allGoods[i - 1].value ;
}
}
}
}
//構造最優解
int j = knapSackCap;
for(int i = allGoods.size();i > 0;--i)
{
if(record[i][j] > record[i - 1][j])
{
knapStackGoods.push_back(allGoods[i - 1]);
j -= allGoods[i - 1].weight;
}
}
}
四、算法複雜性分析
算法中有兩層嵌套for循環,爲此可選定語句j < allGoods[i - 1].weight爲基本語句,其運行時間爲nKnapStackCap * nGoodsNum;則算法時間複雜性爲O(nKnapStackCap * nGoodsNum)。又因爲用到輔助變量record,所以其空間複雜性爲O(nKnapStackCap * nGoodsNum)。
五、實現代碼
#include <iostream>
#include <vector>
using namespace std;
const int nGoodsNum = 5;
const int nKnapSackCap = 10;
class Goods //定義貨物數據類型
{
public:
int weight;
int value;
};
typedef vector<Goods> AllGoods;//定義所有貨物數據類型
typedef vector<vector<int>> RecordTable;//定義記錄表數據類型
//問題求解
void KnapSack_0_1(AllGoods &allGoods,int knapSackCap,AllGoods &knapStackGoods)
{
RecordTable record;//定義記錄表
vector<int> temp;//一個臨時變量,用來初始化record
//將record所有元素初始化爲0
for(int i = 0;i <= allGoods.size();++i)
{
record.push_back(temp) ;
for(int j = 0;j <=knapSackCap;++j)
{
record[i].push_back(0);
}
}
//計算record[i][j]
for(int i = 1;i <= allGoods.size();++i)
{
for(int j = 1;j <=knapSackCap;++j)
{
if(j < allGoods[i - 1].weight)
{
record[i][j] = record[i - 1][j];
}
else
{
if(record[i - 1][j - allGoods[i - 1].weight] + allGoods[i -1].value < record[i - 1][j] )
{
record[i][j] = record[i - 1][j];
}
else
{
record[i][j] = record[i - 1][j - allGoods[i - 1].weight] + allGoods[i - 1].value ;
}
}
}
}
//構造最優解
int j = knapSackCap;
for(int i = allGoods.size();i > 0;--i)
{
if(record[i][j] > record[i - 1][j])
{
knapStackGoods.push_back(allGoods[i - 1]);
j -= allGoods[i - 1].weight;
}
}
}
//獲取物品信息,此處只是將書上例子輸入allGoods
void GetAllGoods(AllGoods &allGoods)
{
Goods goods;
goods.weight = 2;
goods.value = 6;
allGoods.push_back(goods);
goods.weight = 2;
goods.value = 3;
allGoods.push_back(goods);
goods.weight = 6;
goods.value = 5;
allGoods.push_back(goods);
goods.weight = 5;
goods.value = 4;
allGoods.push_back(goods);
goods.weight = 4;
goods.value = 6;
allGoods.push_back(goods);
}
int main()
{
AllGoods allGoods;
AllGoods knapStackGoods;
GetAllGoods(allGoods); //要求同樣重量的物品,價值大的排在前面
//求解
KnapSack_0_1(allGoods,nKnapSackCap,knapStackGoods);
//輸出結果
cout<<"物品個數:"<<nGoodsNum<<endl;
for(int i = 0 ;i < allGoods.size(); ++i)
{
cout<<"重量:"<<allGoods[i].weight<<" 價值: "<<allGoods[i].value<<endl;
}
cout<<"揹包容量: "<<nKnapSackCap<<endl;
cout<<"揹包中可裝入物品:"<<endl;
for(int i = 0;i < knapStackGoods.size();++i)
{
cout<<"重量:"<<knapStackGoods[i].weight<<" 價值: "<<knapStackGoods[i].value<<endl;
}
return 0;
}