从回溯算法到分治界限算法比较最大问题和最小问题

回溯法与分支界限的区别

回溯法:

  • 定义解空间树
  • 深度优先
  • 遍历结构是栈(递归/回溯)
  • 通过约束函数和界限函数剪枝

分支界限

  • 定义解空间树
  • 广度优先、广度优先结合深度优先遍历
  • 遍历方式可以是队列和最优队列
  • 通过约束函数和界限函数剪枝

当采用队列时,与回溯一样,可以一定程度的剪掉一部分的解空间子树。此时遍历方式为广度优先

当采用最优队列时,可以最大程度的“剪掉”更可能多的解空间子树,此事遍历方式为广度优先结合深度优先

原因:

  • 最大问题:界限函数为上限,虽然本层节点的界限函数 大于或等于
    下层该节点的子节点的界限函数(上界),但不确本层其他节点和下层节点的界限函数大小关系,所以采用最优队列取出的节点(当前的最大值),可能是当前层的也可能是下层的。所以解空间的遍历方式是广度优先结合深度优先。

但是值得注意,由于其他层的父节点的上限大于各自的儿子节点和孙子节点等的上限,所以只要最底下一层被取出一个节点,就代表这是整棵解空间树的最大的上限的解,也等效最大装载量的解,最底层再也没有比这个节点大的上限,哪怕它还没被加入优先队列。

  • 同理,最小问题:界限函数为下限,采用最优队列取出的节点(当前的最小值),可能是当前层的也可能是下层的。所以解空间的遍历方式也是广度优先结合深度优先

下面以0/1背包问题、最大装载问题、旅行商问题、流水线调度问题比较回溯算法分治界限算法的两种形式。

最大装载问题

给定n个货物,及其对应的重量w1,w2…wn-1,wn,再载给定一个最大限重量W。求在不超过限重量的情况下最大装载量。

(1)回溯算法:

//r是全局变量

BackTrackLoading(int i)
{
	if( i == n+1){
		if(cw > bestw){
			bestw = cw;
			for(int j = 0; j<n; j++) //记录货物装载情况(1:装	0:不装)
				bestx[ j ] = x[ j ];
		}
	}
	else{
		r = r - w[ i ];
		
		if (cw + w[i] <= W){
			x[ i ] = 1;
			cw = cw + w[i];
			BackTrackLoading( i );
			cw = cw + w[i];
		}
		if(cw + r > bestw ){
			x[ i ] = 0;
			BackTrackLoading( i );
		}
		
		r = r + w[ i ];
	}
}

(2)分治算法

void AddLiveNode(Q, E, B , ch, i )  //添加活结点
{
	b.parent = E;
	b.ch = ch;
	N.up_weight = B;
	N.level = i ;
	N.ptr = b;
	Insert( Q, E);
}

int MaxCostLoading()
{
	i = 1;
	r[ n ]= 0;
	for( int j=n-1; j>=1; j++)	s[j] =  s[j+1] + r[j+1];
	while( i != n){
		if ( cw + w[i]  < W)
			AddLiveNode(Q, E, cw + w[i] + r[i], 1, i );
		
		AddLiveNode(Q, E, cw+ r[i], 0, i );
		N = ExtractMax(Q);
		i = N.level + 1;
		E = N.ptr;
		cw = N.weight - r[i-1];
	}
	for(int j=n; j >= 1; j++){
		bestx[j] = E.Lchild;
		E = E.parent;
	}
	return cw;
}

(2)完整代码:

#include<stdio.h>
#include<queue>
#include<vector>
using namespace std;

#define N 20
#define NUM 100000

typedef struct LiveNode
{
    int up_weight;
	int level;

	LiveNode *parent;
	char ch;

}LiveNode, *pNode;

int n;
int W;
int w[N];
int bestx[N];

LiveNode buffer[NUM];  /*!!为节点提供内存!!*/
int head = 0;


struct comp {
		comp() {}
		~comp() {}
		bool operator()(pNode x, pNode y) {
			return x->up_weight < y->up_weight;
		}
};

priority_queue<pNode, vector<pNode>, comp> Q;
/*!!其中,第二个参数为容器类型。第三个参数为比较函数。*/


void scanff();
void AddLiveNode(pNode pE, int B , char ch, int i); //添加活结点
int MaxCostLoading();

int main()
{
    scanff();
    int bestw = MaxCostLoading();
    printf("最大最大装载量是:%d\n",bestw);

    return 0;
}

void scanff()
{
    printf("货物数量/装载量:");
    scanf("%d%d",&n,&W);

    printf("装载信息:\n");
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
}


int MaxCostLoading()
{
	int i = 1,cw = 0;
	pNode pE = NULL;
	int r[N];
	r[ n ]= 0;

	for( int j=n-1; j>=1; j--){
        r[j] =  r[j+1] + w[j+1];
        printf("r[%d] = %d\n",j,r[j]);
	}

	/*!!思考为什么只要取出解空间树最下一层的一个节点就可以停止了*/
	while( i != n+1){
		if ( cw + w[i]  <= W)
			AddLiveNode(pE, cw + w[i] + r[i], 1, i );

        AddLiveNode(pE, cw+ r[i], 0, i ); 
        /*!!注意这里的”剪枝“,是通过优先队列取最大,取出最大即停止来实现的。*/
        
		pE = Q.top();
        Q.pop();

		i = pE->level + 1;
		cw = pE->up_weight - r[i-1];
        
        //printf("%d->\n",i) ;
	}
	for(int j=n; j >= 1; j--){
		bestx[j] = pE->ch;
		pE = pE->parent;
	}

	return cw;
}

void AddLiveNode(pNode pE, int B , char ch, int i )  //添加活结点
{
	pNode pN = &buffer[head++];

	pN->up_weight = B;
	pN->level = i ;
	pN->parent = pE;
	pN->ch = ch;

	Q.push(pN);
}

0/1背包问题

旅行商问题

流水线调度问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值