// n=5, c=10, w={2, 2, 6, 5, 4}, v={6, 3, 5, 4, 6}的0-1背包问题的最优解和最优值。
#include <iostream>
using namespace std;
#define N 10
typedef struct Bag {
double w[N];//重量
double v[N];//价值
int id[N];//排序后的每个元素的原序号
int x[N];//1表放入背包,0表不放入
int n;//n:物品个数
double c;// c:背包的最大容量
double cw{ 0 };//当前物品总重
double cv{ 0 };//当前物品总价值
double bestv = 0;//当前最大价值
int bestx[N]{0};//最优解
friend double knapspack(double v[], double w[], double cw, int n);
}bag;
typedef struct {
int id;
double danweivalue;
friend double knapspack(double v[], double w[], double cw, int n);
}xingjiabi;
bag mybag{};
xingjiabi s[N]{};
void backtrack(int k);
double knapspack(double v[], double w[], double cw, int n);
void sort(xingjiabi s[], int n);
double bound(int i);
int main()
{
cout << "请输入物品的个数:";
cin >> mybag.n;
cout << "请输入背包的限制容量:";
cin >> mybag.c;
double w[N], v[N];
cout << "请输入每个物品的重量及价值:" << endl;
for (int i = 1; i <= mybag.n; i++)
{
cin >> w[i] >> v[i];
}
knapspack(v, w, mybag.cw, mybag.n);
return 0;
}
//回溯函数 k表示当前处在第几层做选择,k=1时表示决定是否将第一个物品放入背包
void backtrack(int k)
{
//叶子节点,输出结果
if (k > mybag.n) {//找到一个更优的解
if (mybag.cv > mybag.bestv) {//保存更优的值和解
mybag.bestv = mybag.cv;
for (int i = 1; i <= mybag.n; i++)
mybag.bestx[mybag.id[i]] = mybag.x[i];//
}
}
else {//遍历当前节点的子节点
if ((mybag.cw + mybag.w[k]) <= mybag.c) {
mybag.x[k] = 1;
mybag.cw += mybag.w[k];
mybag.cv += mybag.v[k];
backtrack(k + 1);
mybag.cw -= mybag.w[k];
mybag.cv -= mybag.v[k];
mybag.x[k] = 0;
}
if ( bound(k + 1) > mybag.bestv) {
backtrack(k + 1);
}
}
}
double knapspack(double v[], double w[], double cw, int n)
{
double ws{ 0 }; double vs{ 0 };//总质量和总价值
for (int i = 1; i <= n; i++)
{
s[i].id = i;
s[i].danweivalue = v[i] / w[i];
ws += w[i];
vs += v[i];
}
if (ws < mybag.c)
{
cout << "最优值是所有物品价值总和" << endl;
return vs;
}
sort(s, n);
for (int i = 1; i <= n; i++)
{
mybag.v[i] = v[s[i].id];
mybag.w[i] = w[s[i].id];
mybag.id[i] = s[i].id;
}
backtrack(1);
cout << "最优值是:" << mybag.bestv << endl;
cout << "(";
for (int i = 1; i <= mybag.n; i++)
cout << mybag.bestx[i] << " ";
cout << ")";
}
void sort(xingjiabi s[], int n)
{
int k;
for (int i = 2; i <= n; i++)
{
if (s[i].danweivalue > s[i - 1].danweivalue)
{
s[0] = s[i];
for (k = i - 1; !(s[k].danweivalue > s[0].danweivalue); k--)
{
s[k + 1] = s[k];
}
s[k + 1] = s[0];
}
}
}
double bound(int i)
{
double rightbest = mybag.cv;
double cleft = mybag.c - mybag.cw;
while (i <= mybag.n && mybag.w[i] < cleft)
{
cleft -= mybag.w[i];
rightbest += mybag.v[i];
i++;
}
if (i <= mybag.n) { rightbest += mybag.v[i] / mybag.w[i] * cleft; }
return rightbest;
}
/*测试:
n=5,c=10
2 6
2 3
6 5
5 4
4 6
*/
参考:回溯法----0-1背包问题_回溯法求解0-1背包问题_-小透明-的博客-CSDN博客