目錄
一、算法思想
回溯法是一種選優搜索法,按照選優條件深度優先搜索,以達到目標。當搜索到某一步,發現原先選擇並不是最優或達不到目標,就退回一步重新選擇。其核心思想爲“能進則進,進不了則換,換不了則退”。
二、算法要素
1.解空間
確定問題的解的形式,以及對解的分量的顯約束。解空間就是所有可能解組成的空間。
2.解空間結構
解空間樹,有子集樹,排列樹等。
3.搜索解空間
隱約束指對能否得到問題的可行解或最優解做出的約束,也稱爲剪枝函數,包括約束函數和限界函數。
回溯法解題的關鍵是設計有效的顯約束和隱約束。
三、回溯法求解0-1揹包問題代碼
//回溯法求解0-1揹包問題
#include <iostream>
#include <string>
#include <algorithm>
#define M 105
using namespace std;
int i, j, n, W; //n表示n個物品,w表示購物車的容量
double w[M], v[M]; //w[i]表示第i個物品的重量,v[i]表示第i個物品的價值
bool x[M]; //x[i]表示第i個物品是否放入購物車
double cw; //當前重量
double cp; //當前價值
double bestp; //當前最優價值
bool bestx[M]; //當前最優解
//計算上界,查看剩餘物品的總價值和當前已選中物品的價值之和
double Bound(int i)
{
//剩餘物品爲第i~n種物品
int rp = 0;
while (i <= n)
{
rp += v[i];
i++;
}
return rp + cp;
}
//回溯搜索,按照約束條件和限界條件搜索求解
void Backtrack(int t) //t表示當前擴展節點在第t層
{
if (t > n) //已經到達葉子節點
{
for (j = 1; j <= n; j++)
{
bestx[j] = x[j];
}
bestp = cp; //保存當前最優解
return;
}
if (cw + w[t] <= W) //如果滿足約束條件則搜索左子樹,左子樹代表物品被選取
{
x[t] = 1;
cw += w[t];
cp += v[t];
Backtrack(t + 1); //深度優先搜索第t+1層
cw -= w[t]; //迴歸時即向上回溯,要把增加的值減去
cp -= v[t];
}
if (Bound(t + 1) > bestp) //如果滿足限界條件則搜索右子樹
{
x[t] = 0;
Backtrack(t + 1);
}
}
//揹包問題初始化
void Knapsack()
{
//初始化
cw = 0;
cp = 0;
bestp = 0;
double sumw = 0.0; //用來統計所有物品的總重量
double sumv = 0.0; //用來統計所有物品的總價值
for (i = 1; i <= n; i++)
{
sumv += v[i];
sumw += w[i];
}
if (sumw <= W)
{
bestp = sumv;
cout << "放入購物車的物品最大價值爲: " << bestp << endl;
cout << "所有的物品均放入購物車。";
return;
}
Backtrack(1);
cout << "放入購物車的物品最大價值爲: " << bestp << endl;
cout << "放入購物車的物品序號: ";
for (i = 1; i <= n; i++)
{
if (bestx[i] == 1)
cout << i << " ";
}
cout << endl;
}
int main()
{
cout << "請輸入物品的個數n: ";
cin >> n;
cout << "請輸入購物車的容量W: ";
cin >> W;
cout << "請依次輸入每個物品的重量w和價值v,用空格分開:";
for (i = 1; i <= n; i++)
cin >> w[i] >> v[i];
Knapsack();
return 0;
}