装载问题(回溯法)
类似01背包问题,可以用动态规划,但有时候使用回溯法会更加高效。
#include<iostream>
#include<iomanip>
#define GOODS_NUM 4
using namespace std;
/**
* 2021.12.15
* 装载问题
* 迭代回溯,省去O(n)递归栈空间
* 时间复杂度:O(2^n)
*/
/**
* @param w[] 货物的重量数组
* @param c 一艘船的最大限重量
* @param n 货物的数量
* @param bestx[] 最优载重量的解,装了第i个货物则bestx[i]=1;否则bestx[i]=0
*/
template<class Type>
Type maxLoading(Type w[], Type c, int n, int bestx[]) {//迭代回溯法
//返回最优载重量及其相应解,初始化根节点
int i = 1;//当前层,从1开始,而非0
int* x = new int[n + 1];//x[1:i-1]为当前路径。不考虑下标0,所以开辟n+1个空间。
Type bestw = 0,//最优载重量
cw = 0,//当前载重量
r = 0;//剩余载重量
for (int i = 1; i <= n; i++) {
r += w[i];//初始化剩余载重量
}
while (true) {//搜索子树
while (i <= n && cw + w[i] <= c) {//进入左子树
r -= w[i];
cw += w[i];
x[i] = 1;
i++;
}
if (i > n) {//到达叶节点,由于有剪枝回溯,保证每次到达叶节点一定会更新最佳路径
for (int j = 1; j <= n; j++) {
bestx[j] = x[j];
}
bestw = cw;
}
else {//进入右子树
r -= w[i];
x[i] = 0;
i++;
}
while (cw + r <= bestw) {//剪枝回溯,cw+r为当前重量和接下来的所有集装箱之和,即当前的最大可能
i--;
while (i > 0 && !x[i]) {//*****不明白这一步的意思
r += w[i];//只要i要发生变化了,r就会一起发生变化
i--;
}
if (i == 0) {
delete[] x;
return bestw;
}
//进入右子树
x[i] = 0;
cw -= w[i];
i++;
}
}
}
int main() {
int w[GOODS_NUM+1] = { -1,10,30,40,20 },
c = 70,
* bestx = NULL,
bestw;
bestx = new int[GOODS_NUM + 1];
bestw = maxLoading(w, c, GOODS_NUM, bestx);
cout << "bestw:" << bestw << endl;
for (int i = 1; i <= GOODS_NUM; i++) {
cout << setw(3) << w[i];
}
cout << endl;
for (int i = 1; i <= GOODS_NUM; i++) {
cout << setw(3) << bestx[i];
}
delete[] bestx;
return 0;
}