上文已講述了回溯法以及01揹包問題的原理,本文講述如何順序執行解決01揹包問題以及通過模板模式重構軟件。
一、順序執行流程圖
圖1無剪枝函數的01揹包問題順序執行算法流程圖
圖2 有剪枝函數的01揹包問題順序執行算法流程圖
無剪枝函數是通用的深度遍歷算法,爲了減少搜索深度可通過剪枝函數處理完全不可能的分枝。與遞歸方案的區別主要表現在i>=n後需要“回溯”,即用後進先出的方式將物品逐個拿出。
二、執行代碼
遞歸與順序執行方法僅僅是實現方法Backtracking(int i)不同,其餘的完全一致,因此可將具體實現延遲至子類中。模板方法使得自雷可以改變一個算法的結構即可重定義該算法的某些特定步驟,因此採用模板模式。類圖結構如圖3所示:
圖3 回溯法模板模式類圖
代碼1 測試代碼
public static void Main (string[] args)
{
Obj[] objs = new Obj[4];
objs[0] = new Obj() { Weight = 7, Price = 42 };
objs[1] = new Obj() { Weight = 3, Price = 12 };
objs[2] = new Obj() { Weight = 4, Price = 40 };
objs[3] = new Obj() { Weight = 5, Price = 25 };
AbsBacktracking r = new Backtracking_Nonrecursion();
r.W = 10;
r.objs = objs;
Console.WriteLine("Nonrecursion Model:");
r.Backtracking(0);
Console.WriteLine("Recursion Model:");
r = new Backtracking_Recursion();
r.W = 10;
r.objs = objs;
r.Backtracking(0);
Console.Read();
}
代碼2 抽象類 AbsBacktracking
public abstract class AbsBacktracking
{
#region field
protected int m_currentWeight = 0;
protected int m_currentPrice = 0;
#endregion
#region property
/// <summary>
/// 揹包容量
/// </summary>
/// <value>默認20</value>
public int W
{
get;
set;
}
public int n
{
get
{
return objs == null ? 0 : objs.Length;
}
}
/// <summary>
/// 物品,包括重量/價值和數量
/// </summary>
/// <value>The objects.</value>
public Obj[] objs
{
get;
set;
}
#endregion
public abstract void Backtracking(int i);
protected void Printing()
{
Console.Write("<");
for (int i = 0; i < objs.Length; i++)
{
Console.Write(objs[i].Selected ? "1 " : "0 ");
}
Console.WriteLine(">, price: " + m_currentPrice.ToString()
+ "\t weight: " + m_currentWeight.ToString());
}
}
代碼3 類Backtracking_Nonrecursion
public class Backtracking_Nonrecursion:AbsBacktracking
{
public override void Backtracking(int i)
{
while (true)
{
if (i < n)
{
if (m_currentWeight + objs[i].Weight <= W)
{
//將第i個物品放入包中
m_currentWeight += objs[i].Weight;
m_currentPrice += objs[i].Price;
objs[i].Selected = true;
}
}
else
{
//打印路徑
Printing();
i--;
while (i >= 0 && !objs[i].Selected)
{
i--;
}
if (i < 0)
{
return;
}
else
{
//將i從包內拿出
m_currentWeight -= objs[i].Weight;
m_currentPrice -= objs[i].Price;
objs[i].Selected = false;
}
}
i++;
}
}
}
代碼4 代碼片段Backtracking_Recursion
public class Backtracking_Recursion :AbsBacktracking
{
public override void Backtracking (int i)
{
if (i >= n ) {
Printing();
return;
}
if (objs[i].Weight + m_currentWeight <= W) {
m_currentWeight += objs [i].Weight;
m_currentPrice += objs [i].Price;
objs [i].Selected = true;
Backtracking (i + 1);
m_currentPrice -= objs [i].Price;
m_currentWeight -= objs [i].Weight;
}
objs [i].Selected = false;
Backtracking (i + 1);
}
}
代碼5 Obj代碼片段
public class Obj
{
/// <summary>
/// 物品重量
/// </summary>
public int Weight
{
get;
set;
}
/// <summary>
/// 物品價值
/// </summary>
public int Price
{
get;
set;
}
/// <summary>
/// 該物品是否放入包內
/// </summary>
internal bool Selected
{
get;
set;
}
}
三、運行結果:
注:上述實現中沒有添加剪枝函數功能,也沒有添加判斷最優路徑功能,讀者可繼續優化。