一.問題分析
利用優先隊列,以結點的價值上界作爲優先值.這裏要用到的頭文件:<queue>的容器priority_queue.
它的特點是會在內部按特徵值自動排序
當優先隊列的數據結構是struct時寫法如下
struct PState
{
....
//成員函數、構造函數、數據成員部分
friend bool operator <(const PState& a, const PState& b)
{
return a.up < b.up; //up值從小到大--推算-->優先級從小到大
}
};
priority_queue <PState> q;
二.代碼實現
分爲三部分:全局變量 & 上界函數 & 遍歷函數 & 初始化及調用及打印函數
1.全局變量
#include"allh.h"
//採用優先隊列的01揹包問題
//Priority_queue是可排序的queue,top()返回最大值元素
//物品結構
vector<int>W2 = { 2,5,4,2 };
vector<int>V2 = { 6,3,5,4 };
int N2 = (int)V2.size();
int bestp2; //記錄最優值
vector<bool>bestx2; //記錄最優解
int Volume2; //記錄購物車總容量
int sumv2, sumw2; //全部物品總重量和總價值
//性價比結構,用於排序
struct Object
{
int id; //表示順序
double quo; //記錄性價比
};
2.上界函數Bound(),注意數據類型
//上界函數,需要id,rw,cp
double Bound(PState t)
{
double resValue=0;
int num = t.id; //排序後的即將判斷的物品序號
double residue = t.rw; //當前狀態的剩餘容量
while (num < N2 && W2[num] <= residue)
{
resValue += V2[num];
residue -= W2[num];
num++;
}
if (num < N2)
resValue += double(V2[num]) / W2[num] * residue; //01揹包問題在計算上界時,分割計算上界
return resValue + t.cp; //最大上界=狀態當前價值+剩餘空間能裝的最大價值
}
3.遍歷函數,改用q.top()獲取棧頂元素
int Priority_BFS_01Backpack()
{
int t;//當前物品序號
double tup; //保存價值上界
priority_queue<PState>q1;
q1.push(PState(0, sumv2, Volume2, 0));//已有物品總值,剩餘物品總值,剩餘容量,物品序號
while (!q1.empty()) //開始循環
{
PState livenode, lchild, rchild; //每次循環創建當前結點,左子樹,右子樹
livenode = q1.top(); //上一次while循環入隊的左右孩子成爲了新的livenode
q1.pop();
t = livenode.id;
//活結點的約束條件(終止循環條件)
if (t >= N2 || livenode.rw == 0)//物品序號 t=[0,N-1] || 當前狀態的剩餘空間==0
{
//多個可行解中得最優值
if (livenode.cp >= bestp2)
{
bestx2 = livenode.x;
bestp2 = livenode.cp;
}
continue;
}
//對活結點的限界條件
if (livenode.up < bestp2)
continue;
//左子樹
if (livenode.rw >= W2[t]) //約束條件:剩餘容量裝得下當前物品
{
//創建左孩子
lchild.cp = livenode.cp + V2[t];
lchild.rw = livenode.rw - W2[t];
lchild.id = t + 1;
lchild.up = Bound(lchild);
lchild.x = livenode.x; lchild.x[t] = true;
if (lchild.cp > bestp2) //當前解>最優解才更新
bestp2 = lchild.cp;
q1.push(lchild);
}
//右子樹
rchild.cp = livenode.cp;
rchild.rw = livenode.rw;
rchild.id = t + 1;
tup = Bound(rchild);
if (tup >= bestp2)
{
rchild.up =Bound(rchild);
rchild.x = livenode.x;
rchild.x[t] = false;
/*rchild = PState(rchild.cp, rchild.up, rchild.rw, rchild.id);*/ //因爲構造函數,這一行會變回默認值
q1.push(rchild);
}
}
return bestp2;
}
4.調用函數
void Prio_backpack()
{
//初始化
bestp2 = 0;
Volume2 = 10;
bestx2.resize(N2);
sumw2 = accumulate(W2.begin(), W2.end(), 0); //記錄總重量
sumv2 = accumulate(V2.begin(), V2.end(), 0); //記錄總價值
//對性價比排序
vector<Object>S; Object a;
for (int i = 0; i < N2; i++)
{
a.id = i; a.quo = 1.0*V2[i] / W2[i];
S.push_back(a);
}
sort(S.begin(), S.end(), [](Object a, Object b)
{
return a.quo > b.quo;
});
//將排序結果傳給V2[],W2[]
vector<int>tempW = W2;
vector<int>tempV = V2;
for (int i = 0; i < N2; i++)
{
W2[i] = tempW[S[i].id];
V2[i] = tempV[S[i].id];
}
tempV.~vector(); tempW.~vector();
if (sumw2 <= Volume2)
{
bestp2 = sumv2;
cout << "放入購物車的物品最大價值爲: " << bestp2 << endl;
cout << "所有的物品均放入購物車。";
return;
}
//調用函數
cout << "最優值爲: " << Priority_BFS_01Backpack();
cout << endl;
//輸出
cout << "放入的物品爲: ";
for (int i = 0; i < N2; i++)
{
if (bestx2[i])
cout << S[i].id << " "; //輸出原來的序號
}
cout << endl;
}
三.Bug分析
1.因累贅的構造函數導致的不出現最佳策略.
初次調試:
未出現最優策略
調試發現在左右子樹的該語句中,child.x[]繼承了livenode.x[]後又被構造函數初始化爲{0,0,0....} .這一句完全是多餘的
刪除後顯示成功.
2.在Bound()上界函數的定義中, up已經是當前值+剩餘所有值,在調用Bound()時, 不應該再加一次cp
3.bound()中, res的數據類型寫錯了.應該是double
四.算法優化
先看一段代碼
queue<int>q;
q.push(5); q.push(2); q.push(3);
int* p= &q.front();
q.pop();
cout << *p;
輸出是5.
元素5在pop()後仍存在於內存中,僅僅是出隊了而已.
可以用指針指向livenode結點的父結點和左右子結點.得到最優解時一路返回.
3個State* 指針所花費的空間 < 一個bool [ n ]解向量 (n比較大時)