您的位置:首页 > 其它

装载问题 回溯法剪枝过程详解 cw + r > bestw

2018-02-10 14:01 267 查看
因为我参考的文章也是转,暂时没找到真正的作者,如有知原地址,望告之,再加以修正。
1.问题描述:
     有一批共有 n 个集装箱要装上两艘载重量分别为 c1 和 c2 的轮船,其中集装箱 i 的重量为 w[i], 且重量之和小于(c1 + c2)。装载问题要求确定是否存在一个合理的装载方案可将这 n 个集装箱装上这两艘轮船。如果有,找出一种装载方案。
    例如,当n=3,c1=c2=50,且w=[10,40,40]时,可将集装箱1和集装箱2装上一艘轮船,而将集装箱3装在第二艘轮船;如果w=[20,40,40],则无法将这3个集装箱都装上轮船。
下面是摘抄内容:
    没有原图,我根据自己理解随后画了一张,仅供参考:


假定n= 4,w= [ 8 , 6 , 2 , 3 ],c1 = 1 2。解空间树为图1 6 - 2的树再加上一层节点。搜索从根A开始且c w= 0。若移动到左孩子B则c w= 8,c w≤c1 = 1 2。以B为根的子树包含一个可行的节点,故移动到节点B。从节点B不能移动到节点D,因为c w+w2 >c1。移动到节点E,这个移动未改变c w。下一步为节点J,c w= 1 0。J的左孩子的c w值为1 3,超出了c1,故搜索不能移动到J的左孩子。

可移动到J的右孩子,它是一个叶节点。至此,已找到了一个子集,它的c w= 1 0。xi 的值由从A到J的右孩子的路径获得,其值为[ 1 , 0 , 1 , 0 ]。
回溯算法接着回溯到J,然后是E。从E,再次沿着树向下移动到节点K,此时c w= 8。移动到它的左子树,有c w= 11。既然已到达了一个叶节点,就看是否c w的值大于当前的最优c w 值。结果确实大于最优值,所以这个叶节点表示了一个比[ 1 , 0 , 1 , 0 ]更好的解决方案。到该节点的路径决定了x 的值[ 1 , 0 , 0 , 1 ]。从该叶节点,回溯到节点K,现在移动到K的右孩子,一个具有c w= 8的叶节点。这个叶节点中没有比当前最优cw 值还好的cw 值,所以回溯到K , E , B直到A。从根节点开始,沿树继续向下移动。算法将移动到C并搜索它的子树。

通过不移动到不可能包含比当前最优解还要好的解的右子树,能提高函数m a x L o a d i n g的性能。令b e s t w为目前最优解的重量, Z为解空间树的第i 层的一个节点, c w的定义如前。以Z为根的子树中没有叶节点的重量会超过c w + r,其中r=n ?j = i + 1w[ j ] 为剩余货箱的重量。因此,当c w + r≤b e s t w时,没有必要去搜索Z的右子树。

 令n, w, c1 的值与上例中相同。用新的限界函数,搜索将像原来那样向前进行直至到达第一个叶节点J(它是J的右孩子)。bestw 被置为1 0。回溯到E,然后向下移动到K的左孩子,此时b e s t w被更新为11。我们没有移动到K的右孩子,因为在右孩子节点c w = 8,r = 0,c w + r≤b e s t w。回溯到节点A。同样,不必移动到右孩子C,因为在C点c w = 0,r = 11且c w + r≤b e s t w。加强了条件的限界函数避免了对A的右子树及K的右子树的搜索

以上就是我搜集理解的剪枝详细过程,再附上一份可以运行的代码:#include <iostream>
using namespace std;
int c1, c2; //分别载重量
int n; //集装箱数量
int w[100]; //集装箱重量
int cw; //c1当前载重量
int bestw; //c1当前最优载重量
int r; //剩余集装箱重量
int x[100]; //当前解
int bestx[100]; //当前最优解
void Backtrack(int i)
{
if(i > n)
{
//当前解由于最优解,更新之
if(cw > bestw)
{
for(int j = 1; j <= n; j++)
bestx[j] = x[j];
bestw = cw;
}
return;
}

//搜索子树,放入或不放入
r -= w[i]; //剩余容量集合去掉w[i]
if(cw + w[i] <= c1) //可放入,且放入
{
x[i] = 1; //放入
cw += w[i];
Backtrack(i+1);

cw -= w[i];
}
//如果当前c1的载重量+当前节点的子树的最大重量都没有betsw(最优载重量)大,不放入当前的重量
//就不再向当前节点的右子树移动了,即剪枝。
if(cw + r > bestw) //有可能有新的最优载重量
{
x[i] = 0;//向右子树移动
Backtrack(i+1);
}
r += w[i]; //回溯
}
void main()
{
while((cin >> c1 >> c2 >> n) && n)
{
cw = 0; //每组样例初始化
r = 0;
bestw = 0;
for(int i = 1; i <= n; i++)
{
cin >> w[i];
r += w[i];
}
Backtrack(1);
if(r-bestw <= c2)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: