【算法设计与分析】第6章02 分支限界法

算法分析五:回溯法与分⽀限界法 回溯法与分支界限算法的思想、解题步骤、重要概念 阅读详情
AI摘要

本文介绍了分支限界法的设计技术,包括约束条件、剪枝和设计步骤。通过装载问题、背包问题和旅行商问题三个实例,详细阐述了分支限界法的计算模型、算法设计与描述,利用上界函数和限界值进行剪枝,以加快算法执行速度。

目录

分支限界法的设计技术

分支限界法:

 约束条件

 剪枝

 分支限界法的设计步骤

思考题:

【例6-6】装载问题。

 计算模型

【例6-7】背包

 问题分析

 问题分析

计算模型

 计算模型

 算法设计与描述

代码:

思考:

【例6-8】旅行商问题(Traveling Salesman Problem,TSP):

 问题分析

 计算模型

 算法设计与描述


分支限界法的设计技术

分支限界法(区别):

限界值和上界函数

分支限界法:

增加约束条件,剪掉解空间中更多分支, 加快算法的执行速度。

 约束条件

(1)上界函数:用来求得以当前结点为根的可行性解可能达到的极值【估测】。

(2)限界值:搜索到某一结点时,已经得到可行解或可能包含可行性解的最优值。

(3)评价函数:判定当前所获得路径或值是否为解的函数

 剪枝

(1)该结点的上界小于界限值,即再往下搜索也不可能有更优的值。【以该节点为根的子树】

(2)该结点无法代表任何可行解,因为它已经违反了问题的约束,不能满足评价函数。

(3)该节点代表的可行解的子集只包含一个单独的点。

 分支限界法的设计步骤

(1)建立上界函数,函数值是以该结点为根的搜索树中的所有可行解在目标函数上求得值的上/下界。【不通过搜索来判断,一个估计】

(2)求得限界值,即当前巳经得到的可行解的目标函数的最大值。

(3)依据剪枝条件,停止分支的搜索,向上回溯到父结点。

(4)限界值的更新:当得到的可行解的目标函数值大于/小于当前限界值时,更新之。

思考题:

 回溯:评价函数,即当前路径或值是否为解。

分支限界:上界函数,界限值,评价函数。

【例6-6】装载问题。

有n个集装箱要装上一艘载重量为c的

轮船,其中集装箱i的重量为wi 。在装载体积不受限制的情

况下,找出一种最优装载方案,将轮船尽可能地装满。

问题分析:

 实例:船的载重量c=10,4个集装箱,重量W={7,4,2,6}。

c: 船的载重量。        x[]:左儿子标识。

w[]: 集装箱重量。        s:剩余总重量。

lw: 已装船总重量。

bestw:当前装船最优值,限界值。

pw: 预装重量,pw=lw+w[i]

评价函数:预装集装箱重量+已装重量<=船的载重   lw+w[i]<=c

上界函数:剩余重量+已装重量>限界值 s+lw > bestw

【深度遍历】如果左子树能装,就一直装,不考虑右子树

【限界】类似广度优先,必然要分层,引入队列

 

评价函数:lw+w[i]<c 上界函数:s+lw > bestw

 计算模型

结点的构造如下:

class QNode
{
	template<class T>
		friend int EnQueue(Queue<QNode<T>*>&Q, T wt, int i, int n,
			T bestw, QNode<T>*E, QNode<T>*&bestE, int bestx[], bool ch);
	template<class T>
		friend Type MaxLoading(Type w[],Type c, int n, int bestx[]);
public:
	QNode *parent; //父结点
	bool LChild; //左孩子标志,是左孩子值为真,否则为右孩子。
	Type weight; //结点权值
};

算法设计与描述

//入队操作
int EnQueue(Queue<QNode<Type>*>&Q,Type pw,int i, int n, Type bestw, QNode<Type>*E,QNode<Type>*&bestE,int bestv[], bool x)
{ 
	if(i==n){//到达叶结点
		if (pw==bestw) {//找到最优值
			bestE=E;
			bestv[n]=x;
		}
	return 1; 
	}
	QNode<Type> *b;
	b=new QNode<Type>;
	b->weight=pw;
	b->parent=E;
	b->LChild=x;
	Q.Add(b);
	return 1;	
}


int main(int argc, const char * argv[]) {
	float c=10;
	float w[]={0,7,4,2,6};//7,4,2,6
	int x[N+1];
	float bestw;
	bestw=MaxLoading(w,c,N,x);
	
	cout<<"result is: "<<endl;
	for(int i=1;i<=N;i++)
		cout<<x[i]<<" ";
	cout<<endl;
	
	cout<<"best weight s:"<<bestw<<endl;
	return 0;
}

0

template<class Type>
Type MaxLoading(Type w[],Type c, int n, int bestv[])
{ 
	Queue<QNode<Type>*> Q;
	Q.Add(0);
	int i=1;
	Type lw=0,bestw=0,s=0;
	for(int j=2;j<=n;j++) 
		s+=w[j];
	QNode<Type> *E=0, *bestE;
	while(true)
	{ 
		Type pw=lw +w[i];//预装重量
		if(pw<=c)
		{ 
			if(pw>bestw) 
				bestw=pw; //选择集装箱
			EnQueue(Q,pw,i,n,bestw,E,bestE,bestv,true);
		}
		if(lw+s>bestw){//剪枝
			EnQueue(Q,lw,i,n,bestw,E,bestE,bestv,false);
		}//从队列Q中输出一个结点
		Q.Delete(E);
		if(!E) {
			if(Q.IsEmpty())
			break;
			Q.Add(0); //加入分层结点
			Q.Delete(E);
			i++;
			s-=w[i];
		}
		lw =E->weight;
	}
	for(int j=n-1;j>0;j--)
	{
		bestv[j] =bestE->LChild;
		bestE =bestE->parent;
	}
	return bestw;	
}

【例6-7】背包

已知有n件物品,物品i的重量为wi 、价值为pi ,此物品可以有

多个。现从中选取一部分物品装入一个背包内,背包最多可容纳的总重

量是m,如何选择才能使得物品的总价值最大。为了追求价值最大化,

同一物品可以装多件。

问题分析

实例:背包总承重为10

 (1)允许同一物品装载多个,非0-1背包问题。

(2)背包总承重10,最多可以装载5个A,3个B,2个C个,1个D。

(3)按每个物品单位重量的价值(vi /wi )从大到小排列,得到如下表达式:

其中,x1 代表D的数量,x2 代表物品C,x3 代表物品B的数量,x4 代表物 品A的数量。

问题分析

(4)搜索到(x1 , x2 ,…, xk)结点时的上界用下列函数求得:

(5)剪支

计算模型

(1)数据结构

 背包容量BTotal=m,物品品种数量n,选择树的层数为level。物品的特

征:

typedef struct{
	char name;//物品名称
	float v;//商品价值
	float w;//商品重量
	float key;// v/w
}goods;

物品:goods g[]

 当前背包所装物品重量bagW;当前背包所装物品总价bagP;

当前装载物品bagG[],下标表示物品编号,值表示物品数量;

当前最优总价值bestV,当前最优解bestS[],当前分支的上界ULimit。

计算模型

(2)递归出口

(3)迭代公式

其中,i表示第level种物品装入的数量,i∈[0,BTotal/g[level].w],。

回溯时需要恢复现场,所需要的迭代公式如下,

bagW=bagW-i*g[level].w;

bagP=bagP-i*g[level].v;

bagG[level]=0;

算法设计与描述

输入:品种数量N;背包容量BTotal;物品描述

输出:最优总价值bestV和最优解bestS[]

knapsack(int level) //深入的层数
{ 
	int i,maxG;
	float ULimit=0;
	int j=0;
	if(level > n){
		if(bagP>bestV){
			for(i=0;i<n;i++)
				bestS[i]=bagG[i];
			bestV=bagP;
		}
		return;	
	}
	maxG=BTotal/g[level].w;
	ULimit=bagP+(BTotal-bagW)*g[level].key;

	if(bestV>=ULimit) return;//剪枝
	for(i=maxG;i>=0;i--)
	{ 
		if(bagW+i*g[level].w<=BTotal)
		{ 	
			bagW=bagW+i*g[level].w;
			bagP=bagP+i*g[level].v;
			bagG[level]=i;
			j=level;
			if(bagW==BTotal)//背包已装满
				for(j=j+1;j<n;j++) 
					bagG[j]=0;
			knapsack(j+1);
			bagW=bagW-i*g[level].w;
			bagP=bagP-i*g[level].v;
			bagG[level]=0;
		} 
	}
}

代码:

#include<bits/stdc++.h>
using namespace std;
//例6-7
//每种物品多个,求最大价值 
//深度优先搜索 

//数据结构
typedef struct{
	char name;//物品名称
	float v;//价值
	float w;//重量
	float key;//单位重量的价值 v/w 
}goods;

const int n=4;
goods g[n];



float bagW=0;//当前所装重量
float BTotal=10;//剩余总重量 

float bagP=0;//当前总价值
int bagG[n]={0};//下标表示物品编号,值表示物品数量;
float bestV=0;//当前最优总价值,
int bestS[n];//当前最优解,
float ULimit=0;//当前分支的上界。

void knapsack(int level) //深入的层数
{
	int i,j=0,maxG;
	float Ulimit=0;
	if(level>n)//递归出口 
	{
		if(bagP> bestV)//大有原最优 
		{//更新 
			for(int i=0;i<n;i++)
			{
				bestS[i]=bagG[i];
			}
			bestV=bagP;//最优价值 
		} 
		return; 
	}
	//
	maxG=BTotal/g[level].w;//整数 可能最大数目 
	//上界 
	ULimit=bagP+(BTotal-bagW)*g[level].key;
	//已装载价值 + (剩余容量)*当前最大单位价值 
	if(bestV>=ULimit)return;//剪去这枝 
	
	for(i=maxG;i>=0;i--)
	{
		if(bagW+i*g[level].w <=BTotal )
		{
			bagW += i*g[level].w;
			bagP+=i*g[level].v;
			bagG[level]=i;//所装数目
			
			j=level;
			
			if(bagW==BTotal)//背包满 
			{
				for(j++;j<n;j++)
				{
					bagG[j]=0;//之后都不装 
				 }
			} 
			//递归 下一层
			knapsack(j+1);
			//回溯 恢复现场 
			bagW -= i*g[level].w;
			bagP-=i*g[level].v;
			bagG[level]=0;//所装数目
			
		}
	}
	
}




int main()
{
	g[0].name='A';
	g[0].w=2;
	g[0].v=1;
	
	g[1].name='B';
	g[1].w=3;
	g[1].v=3;
	
	g[2].name='C';
	g[2].w=4;
	g[2].v=5;
	
	g[3].name='D';
	g[3].w=7;
	g[3].v=9;
	
	for(int i=0;i<n;i++)
		g[i].key= 1.0*g[i].v/g[i].w; 
	//按单位价值排序 从大到小
	for(int i=0;i<n-1;i++)
	{
		for(int j=0;j<i;j++)
			if(g[j].key < g[j+1].key )
			{
				char tname=g[j].name;
				g[j].name=g[j+1].name;
				g[j+1].name=tname;
				float t=g[j].v;
				g[j].v=g[j+1].v;
				g[j+1].v=t;
				t=g[j].w;
				g[j].w=g[j+1].w;
				g[j+1].w=t;
				t=g[j].key;
				g[j].key =g[j+1].key ;
				g[j+1].key =t;
			}
	} 
	for(int i=0;i<n;i++)
	{
		cout<<g[i].name<<endl;
	}
	knapsack(0);
	cout<<bestV<<endl;
	for(int i=0;i<n;i++)
	{
		cout<<g[i].name <<":"<< bestS[i]<<endl;
	}
	return 0;
} 
/*
11
B:2
C:1
A:0
D:0
*/


 

思考:

【例6-8】旅行商问题(Traveling Salesman Problem,TSP):

有若干个城市,任何两个城市之间的距离都是确定的,现要求一旅行商从某城市

出发,必须经过每一个城市且只能在每个城市逗留一次,最后回到原出

发城市,问事先如何确定好一条最短的路线使其旅行的费用最少。

问题分析

以有5个城市的旅行商问题为例,用分支限界法求解问题,如图所示。

(1)上界:

起点到终点一直是取当前最短边,终点回到起点的边长度不可确定,因此是一个比较好的解。

(2)下界:

每一个点都设一条出边,一条入边。

每个顶点 入边最小值和入边最小值加起来,再除以2 ,就得到一个理想化的下界。

(3) 界函数

①将走过的路径和节点看成一个带权值的结点A【A-B的路径 看成新的结点A'】,加入到图中,取代原来的结点与线段,构成新图,并且将后加入的结N点作为S的带权结点的离开结点,N引领的分支被称为离开分支;【比如此时 B邻接顶点中-C最短,离开结点就是C,而回到A也是C最短,进入结点也是C,进入边也就找到了】

②利用(2)中的方法,计算新图的low值,称为离开分支的lb (low bound)。

计算方法如下

lb=( (已经历路程)*2+新结点的进入边长度 +新结点离开边长度+其它每个结点的两个最短边长度)/2

其中,新结点的进入边是以起始结点为终点的最短边,新结点的离开边是以最晚加入新结点的结点为起点的最短边。

显然,如果lb>up,则此分支中不可能有高于up的最优解,不用再对此分支进行遍历;

③当沿某一个分支执行到叶子结点时,得到一个新的解,如果此解小于up,则可以用此解取代up的值,作为新的上界。【更新up】

④ 算法的改进:考虑到分支的lb值越小越有可能得到问题的最优解,因而可以引进一种数据结构—优先级队列,它可以按关键字的大小排列,如按从小到大顺序,最小的结点放在队头,依次排列,关键字相等的按先进先出的次序排列。

引进优先级队列有两个好处:一是可以更快地找到问题当前的最优解,更早地更新up值,剪掉更多

的分支;二是当找到的解小于队首结点的lb时,则找到了本问题的最优解,可以终结算法。

为什么?

每一次运算都把最小值排在最前。

因为lb存的是所有分支的最小值,队首是lb的最小值,因此就是本问题的最优解。

设A是起始点,A入队。

【考察A的所有相邻节点】

(A,B).lb = (AB*2+BC离开边+进入边CA+(C的两条最短边CA+CE)+(D的两条最短边DC+DE)+(E的两条最短边EC+ED))/2

= (4*2+7+2+(2+3)+(5+4)+(3+4))/2=19  //小于UP值,AC可以入队

(A,C).lb = (AC*2+CE+BA+(BA+BC)+(DC+DE)+(EC+ED))/2

= (2*2+3+4+(4+7)+(5+4)+(3+4))/2=19  //小于UP,入队

(A,D).lb = (AD*2+DE+CA+(BA+BC)+(CA+CE)+(EC+ED))/2 = 21  //<=UP 入队

(A,E).lb = (AE*2+EC+CA+(BA+BC)+(CA+CE)+(DC+DE))/2 = 24 //> up = 21  没有意义,被剪去

入队之后【排队 】

AB边出队

(A,B,C).lb=((AB+BC)*2+CE+DA+(DA+DE)+(EC+ED))/2+1=24【向上取整】

(A,B,D).lb=((AB+BD)*2 +CA+DE +(CA+CE)+(EC+ED))/2=21

(A,B,E).lb=((AB+BE)*2+CA+EC+(CA+CE)+(DC+DE))/2=24

AC出队

(A,C,B).lb=((AC+CB)*2+BD+DA+(DA+DE)+(EC+ED))/2=24

(A,C,D).lb=((AC+CD)*2+DE +BA +(BA+BC)+(EC+ED))/2=20

(A,C,E).lb=((AC+CE)*2+EA+BA+(BA+BC)+(DC+DE))/2=19

【ACE,ABD 入队最小值在队首】

【ACE出队】

(A,C,E,D).lb=((AC+CE+ED)*2+DB+BA+(BA+BC))/2=21

 (A,C,E,B).lb=((AC+CE+EB)*2+BD +DA+(EC+ED))/2=27

【此时ACDE只差一个顶点 】

 【ACD出队】

【AD出队】

(A,D,B).lb=((AD+DB)*2+BC+CA+(CA+CE)+(EC+ED))/2=25

(A,D,C).lb=((AD+DC)*2+CE +BA +(BA+BC)+(EC+ED))/2=24

(A,D,E).lb=((AD+DE)*2+EC+CA+(BA+BC)+(CA+CE))/2=21

【ABD出队】

(A,B,D, C).lb=((AB+BD+DC)*2+CE+EA+(EC+ED))/2=27

(A,B,D, E).lb=((AB+BD+DE)*2+EC +CA+(CA+CE))/2=21

(A,C,E,D,B,A)=AC+CE+ED+DB+BA=21

计算模型

(1)所需要的数据结构

const int INF=10000000; //无穷大,表示两结点间没有直达的边

结点个数:n

上界与下界:low, up

不同城市之间线路的代价(权值)cost[][]、城市信息city[]

一个优先级队列q,并提供如下的结点信息:

struct node
{
	int st; //路径的起点
	int ed; //离开结点,当前路径的终点
	int k; //走过的结点数
	int sumv; //经过路径的总长度
	int lb; //每个分支的下界
	bool vis[]; //本路径中已经走过结点的标识
	int path[]; //记录经过的结点编号
};

(2)贪心算法初始化TSP问题的上界up

(3)求TSP问题的下界low

(4)每个分支的下界lb

(5)计算方法

① 计算up值。 ② 计算low值

③ 设 置 起 始 结 点 start , 令 st=ed=start , start.vis[1]=1, start.k=1, start.lb=low,

start.path[1]=1;

④ 按广度优先的方式逐步加入start临接点nexti,令ed=nexti,更新相关信息,计

算lb的值,若next.lb>up,则淘汰之(剪枝),否则加入队列,直到求出最优解

⑤ 当找到一个解时,用这个解的值与队首的lb值相比较,若此解小于或等于队

首的lb值,则意味着此解已是最优解,终止运算,输出结果。

算法设计与描述

输入:城市数量n,距离矩阵cost[][],城市名称city[]

输出:最优路径path[],最优值mind

#include<iostream>

using namespace std;
//分支限界法
const int INF=10000000; //表示无穷大
int low, up, n, used[20], cost[20][20];
char city[20];

struct node
{ 
	bool vis[20];
	int st;//路径的起点
	int ed;//路径的终点
	int k;//走过的点数
	int sumv;//经过路径的距离
	int lb;//目标函数的值
	int path[20];
	//重载运算符”<”
	bool operator<(const node &p)const
	{ 
		return lb>p.lb; 
	} 
};

priority_queue<node> q; //优先级队列
struct node answer; //存储最优解变量

//用贪心算法和深度优先,求上界 
int get_up_helper(int v,int j,int len)
{ 
	int minE=INF,pos;
	if(j==n) return len+cost[v][1];
	
	for(int i=1;i<=n;i++)//当前结点的最小邻接边 
	{ //采用贪心算法取权值最小的边
		if(used[i]==0 && minlen>cost[v][i])
		{ 
			minE=cost[v][i]; 
			pos=i;//
		}
	}
	used[pos]=1;
	return get_up_helper(pos,j+1,len+minE);
} //这是一个深度优先算法

//求上界 
void get_up()
{ //user[]为结点状态伴随数组
	used[1]=1; //1表示已访问;0表示未访问
	up=get_up_helper(1,1,0);
}

//计算下界
void get_low()
{ 
	low=0;
	for(int i=1;i<=n;i++)//遍历每一个结点 
	{ 
		int temp[20];
		for(int j=1;j<=n;j++)//遍历 
		{ 
			temp[j]=cost[i][j]; 
		}
		sort(temp); 
//		sort(temp+1,temp+1+n);
		low = low+temp[1]+temp[2];//第一小 和 第二小 
	}
	low=low/2;
} //计算上界

//求由p引导的分支的下界
int get_lb(node p)
{ 
	int ret=p.sumv*2; //已遍历城市距离
	
	int min1=INF,min2=INF,pos;
	//最小入边 
	//从起点到最近未遍历城市的距离
	for(int i=1;i<=n;i++)
	{ 
		if(p.vis[i]==0&&min1>cost[p.st][i])
		{ 
			min1=cost[p.st][i]; 
			pos=i; 
		}
	}
	ret+=min1;
	//最小出边 
	//从离开结点到最近未遍历城市的距离
	for(int i=1;i<=n;i++)
	{ 
		if(p.vis[i]==0&&min2>cost[p.ed][i])
		{ 
			min2=cost[p.ed][i]; 
			pos=i; 
		}
	}
	ret+=min2;

	//进入并离开每个未遍历城市的最小成本
	for(int i=1;i<=n;i++)
	{ 
		if(p.vis[i]==0)
		{ 
			int temp[n];
			min1=min2=INF;
			for(int j=1;j<=n;j++)
			{ 
				temp[j]=cost[i][j]; 
			}	
			sort(temp+1,temp+1+n);
			ret+=temp[1]+temp[2];
		} 
	}
	//向上取整
	ret= ret%2==0?(ret/2):(ret/2+1);
	return ret;
}



int solve() //求解过程
{
	int ret=INF;
	get_up(); //求上界
	get_low();
	node start;
	start.st=1; start.ed=1; start.k=1;
	start.sumv=0; start.lb=low; 
	start.path[1]=1;
	for(int i=1;i<=n;i++)
	{ 
		start.vis[i]=0;
		start.path[i]=0;
	}
	
	start.vis[1]=1; 
	q.push(start);
	node next,temp;
	
	while(!q.empty())
	{ 
		temp=q.top(); 
		q.pop();
		//(1)如果只剩最后一个点了
		//不算出发顶点的倒数第二个 
		if(temp.k==n-1)
		{
			int pos=0;
			for(int i=1;i<=n;i++)
			{ 	
				if(temp.vis[i]==0)
				{ 
					pos=i; 
					break; 
				}
			}
			if(pos==0)
				break;
			//实际的值 
			int ans=temp.sumv+cost[pos][temp.st]+cost[temp.ed][pos];
			temp.path[n]=pos;//存最后一个结点
			temp.lb=ans;//更新结点的上界
			node judge=q.top();

			//(2)如果当前的路径和比所有分支
			//上界都小,则找到最优解
			if(ans<=judge.lb)
			{ 
				ret=min(ans,ret);
				answer=temp;//返回答案
				break;
			}
			//否则求其他可能的路径更新上界
			else if (up>=ans) 
			{ 
				up=ans;
				answer1=temp;
			}
			ret=min(ret,ans);
			continue;
		} 
	}
	//
	for(int i=1;i<=n;i++)
	{ 
		if(temp.vis[i]==0)//找到么有访问的相邻顶点 
		{ 
			//复制 刚才访问的起始点 
			next.st=temp.st;
			//在原来的距离 再加上 一个距离 
			next.sumv=temp.sumv	+cost[temp.ed][i];
			//记录一下最后 入列的这个顶点的编号 
			next.ed=i;
			//顶点数+1 
			next.k=temp.k+1;
			//记录该路径 编号 
			for(int j=1;j<=n;j++)
			{
				next.vis[j]=temp.vis[j];
				next.path[j]=temp.path[j];
			}
			//更新值 
			next.vis[i]=1;
			next.path[next.k]=i;//路径统计
			next.lb=get_lb(next);//新的下界 
			if(next.lb<=up) //否则剪枝
				q.push(next);
		} 
	}
	return ret;
}

算法设计与分析复习笔记第六章分支限界法 分支限界法的基本思想分支限界法类似于回溯法,也是一种在问题的解空间树T中搜索问题解的算法。但在一般情况下,分枝限界法与回溯法的求解目标不同。回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分枝限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解。所谓“分枝”就是采用广度优先的策略,依次搜索活结点的所有分枝,也就是所有相邻结点。求最优解时,选择哪一个子结点? 阅读详情

相关推荐

0-1背包问题-分支限界法(优先队列分支限界法)

算法的思想 首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。 在优先队列分支限界法中,结点的优先级定义为:以结点的价值上界作为优先级(由bound函数计算出) 步骤 算法首先根据基于可行结点相应的子树最大价值上界优先级,从堆中选择一个节点(根节点)作为当前可扩展结点。 检查当前扩展结点的左儿子结点的可行性。 如果左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。 当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界函数约束时,才将它加入子集树和活结点优先队

迷雾总会解 6万+

算法小课堂(九)分支限界法

分支限界法是一种求解最优化问题的算法,常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。其基本思想是把问题的可行解展开,再由各个分支寻找最佳解。在分支限界法中,分支是使用广度优先策略,依次生成扩展结点的所有分支。限界是在结点扩展过程中,计算结点的上界,搜索的同时剪掉某些分支。

qq_62377885的博客 1万+

算法分析与设计——6 分支限界法

活结点表”存储了所有已经生成但其子结点尚未被完全探索的结点。只要你能识别出是“最优化问题”,就可以套用这个框架,然后专注于设计问题的。它能自动地根据我们定义的“优先级”(即成本或价值的估计值)对活结点进行排序,使得我们每次都能轻松取出最优的结点。要掌握分支限界法,就要理解它的三个核心部件。这三个部件协同工作,构成了整个算法的骨架。这是分支限界法的灵魂!限界函数的设计直接决定了算法的效率。想象你在一个巨大的迷宫里寻找最短的出口路径(最优化问题)。让我们用上面的框架思想来分析几个经典问题。

Rhett_Butler0922的博客 955

分支限界法

分支限界法的基本思想: 求解目标:分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。 搜索方式:以广度优先或以最小耗费优先的方式搜索解空间树。分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。 在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,...

iron的博客 4万+

分支限界法(算法分析与设计)

0.概念 分支限界法常以广度优先(队列式(先进先出)分支限界)或以最小耗费(最大效益)优先的方式(优先队列分支限界)搜索问题的解空间树。 在分支限界法中,每一个活结点只有一次机会成为扩展结点。活结点一旦成为扩展结点,就一次性产生其所有儿子结点。在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。此后,从活结点表中取下一结点成为当前扩展结点,并重复上述

脚踏实地,仰望星空 8365

趣学算法系列-分支限界法

趣学算法系列-分支限界法声明:本系列为趣学算法一书学习总结内容,在此推荐大家看这本算法书籍作为算法入门, 原作者博客链接,本书暂无免费电子版资源,请大家支持正版第六章 分支限界法 在树搜索法中,从上到下为纵,从左向右为横,纵向搜索是深度优先,而横向搜索是广 度优先。前面讲的回溯法就是一种深度优先的算法。分支限界法就是以广(宽)度优先的树搜索方法 - 分支限界法的算法思想

那远远的云端 7573

《算法设计与分析》期末不挂科

考前知识点整理算法分析基础算法的定义算法正确性算法的性质程序的定义程序与算法的区别算法设计和分析的步骤复杂度分析算法的时间复杂性算法渐近复杂性渐近分析的记号渐近上界记号渐近下界记号非紧上界记号非紧下界记号紧渐近界记号意义算法分析中常见的复杂性函数 我们学校开设的这门课,过于理论,实践太少,考试不会太难,一起学习,一起不挂科! 但是算法平时一定要练哦!加油! 算法分析基础 算法的定义 算法是指解决问题的一种方法或一个过程。 算法是若干指令的有穷序列。 算法正确性 对每一个输入实例算法都能终止,并给出

全网粉丝30W+,优快云、稀土掘金人工智能领域签约作者,人工智能、大模型、Python 爬虫,商务合作,请私信沟通。 8万+

从稳压管到LDO芯片:一张图看懂线性电源的‘内卷’进化史

本文追溯了从稳压管到LDO芯片的线性电源技术进化历程,详细解析了各阶段的技术突破与局限。重点探讨了现代LDO在超低功耗、高精度控制等方面的创新,并提供了实战选型的黄金法则,帮助工程师在电子设备小型化与低功耗化趋势中做出最优选择。

weixin_42525551的博客 205

算法分析与设计:分支限界法

一、分支限界法 1. 基本思想 分支是使用广度优先策略,依次生成扩展结点的所有分支。 限界是在结点扩展过程中,计算结点的上界,搜索的同时剪掉某些分支。 分支限界法就是把问题的可行解展开,再由各个分支寻找最佳解。 与回溯法类似,分支限界法也是在解空间中搜索得到解; 不同的是,分支限界法会生成所有扩展结点,并舍弃不可能通向最优解的结点,然后根据广度优先/最小耗费优先,从活结点中选择一个作为扩展结点,使...

AlexTu的博客 2万+

Android蓝牙Mesh组网实战:从零搭建智能灯光控制系统(附完整代码)

本文详细介绍了基于Android平台开发蓝牙Mesh智能灯光控制系统的完整实战流程。从环境搭建、核心概念解析,到设备扫描配网、网络初始化、模型配置与消息通信,提供了完整的代码实现和调试技巧。重点讲解了如何使用开源Mesh库实现设备发现、分组控制、场景管理等核心功能,并分享了实际项目中遇到的典型问题与解决方案。

weixin_29174013的博客 654

逆向工程师的Shopee爬虫避坑指南:af-ac-enc-dat参数生成原理与反反爬策略

本文深入剖析了Shopee电商平台的反爬虫加密机制,重点解析了核心请求头参数x-sap-ri、x-sap-sec及af-ac-enc-dat的生成原理与联动关系。文章从逆向工程视角,提供了定位加密代码、分析算法逻辑的实战方法,并分享了基于JS执行环境模拟与无头浏览器控制的可持续反反爬策略,为高效、稳定地采集数据提供专业指南。

weixin_28607671的博客 1493

算法设计与分析之分支限界法

文章目录前言一、分支限界法的基本思想总结 前言         大家好,越努力,越幸运,我是程序猿小猿。本篇文章小猿将跟您分享算法设计与分析中的分支限界法,希望对您有所帮助。 一、分支限界法的基本思想 总结 知识点总结 结语         对分支限界法的介绍就到这里啦,希望这篇文章能给予你一些帮助,感谢各位人才的:点赞、收藏和评论,我们下次见。

qq_44075108的博客 1万+

经典损失函数——均方误差(MSE)和交叉熵误差(CEE)的python实现

损失函数(loss function)用来表示当前的神经网络对训练数据不拟合的程度。这个损失函数有很多,但是一般使用均方误差和交叉熵误差等。 1.均方误差(mean squared error) 先来看一下表达式: 用于将的求导结果变成,是神经网络的输出,是训练数据的标签值,k表示数据的维度。 用python实现: def MS...

Answer3664的博客 1万+

【算法设计与分析】期末复习

【算法设计与分析】期末复习

manerzi的博客 1万+

基于Linux的PCIe设备驱动开发入门到精通-- 11.2 锁机制与并发控制 (自旋锁、互斥锁) 11.3 性能瓶颈分析与优化建议

本文探讨Linux驱动开发中的性能优化方法论,重点聚焦并发控制与性能瓶颈分析。从锁机制选择(自旋锁、互斥锁等)到工具使用(ftrace、perf),详细介绍了定位锁竞争和CPU热点的实战步骤,包括ftrace事件跟踪和perf火焰图分析。针对常见性能瓶颈(CPU/内存/I/O/锁竞争),提出了锁粒度优化、无锁结构替换、中断处理优化等解决方案。强调性能优化需要理论结合实践,通过"假设-测量-验证"的循环持续迭代。为开发者提供了一套从问题定位到解决的完整方法论,助力开发高性能工业级驱动。

MHD0815的博客 126

计算机视觉入门:从像素到理解的旅程

计算机视觉入门:从像素到理解的旅程,计算机视觉是人工智能领域的核心方向之一,旨在让计算机具备理解和解释视觉信息的能力。本文以系统化的知识框架,结合 Python 代码与经典案例,带您从像素级操作逐步掌握计算机视觉核心技术。涵盖图像处理、特征工程、传统机器学习到深度学习模型,助您构建从基础到进阶的完整能力体系。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 18万+

算法设计与分析——分支限界法

文章目录1. 分支界限法1.1 基本思想1.2 搜索策略1.3 队列式1.4* 优先队列式2. 典型案例2.1 装载问题2.1.1 问题描述2.1.2 约束函数和限界条件2.1.3 队列式分支界限法求解(案例解释)2.1.4 案列二2.1. 代码实现3. 回溯法与分支限界法异同c++中 friend的用法 1. 分支界限法 1.1 基本思想 对有约束条件的最优化问题的所有可行解(数目有限)空间进行搜索。该算法在具体执行时,把全部可行的解空间不断分割为越来越小的子集(称为分支),

krain的博客 6363

15、分布式参数热交换器:建模、动态特性与控制

本文综述了分布式参数热交换器的建模、动态特性和控制方法。从热交换器的基本方程推导出发,探讨了一维流动、流动强迫、错流、螺旋和旋转再生式热交换器的建模方法,并分析了其动态行为和频率响应中的共振现象。同时,介绍了热交换器的模拟方法,包括数值模拟和近似方法,并讨论了多种控制策略,如常规控制、最优控制、数字控制以及自适应控制。最后,总结了热交换器研究的发展历程,并展望了未来的研究方向,包括建模精度提升、控制算法优化和跨学科融合等。

w8x9y0z1的博客 123

算法设计与分析笔记5———回溯法与分支限界法

结合例题介绍了回溯法与分支限界法

半熟芝士的博客 3849

thinkphp6/8 - 实现评论区发表评论、回复评论功能数据库表设计与整个逻辑思路过程分析,tp5 评论盖楼等功能(附带源码稍加改造即可使用)

tp5评论区数据库表设计,tp5发表回复评论功能设计,thinkphp5如何做评论功能,tp5 php评论区实现发表回复删除评论,tp5评论区盖楼源码及数据表设计思路,thinkphp评论功能详细教程,thinkphp实现评论与回复功能,thinkphp实现文章评论和回复评论功能,tp5实现评论回复功能,thinkphp如何实现评论区,tp5网站PC移动端h5怎么做评论区发表留言及回复功能,thinkphp实现评论区插件,thinkphp评论区相互发表回复功能详细教程,thinkphp有没有评论区的组件插件

✅ 精选推荐文章 3159

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【算法分析与设计】分支限界法(上)

计算机算法分析与设计中有关分支限界法的讲解,主要包括学习要点、分支限界法的基本思想、单源最短路径问题、装载问题、布线问题、0-1背包问题

東方緋想天 1429

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

【计算机算法设计与分析】6-5 最小重量机器设计问题(C++_回溯法/分支限界法)

问题描述 设某一机器由 n 个部件组成,每一种部件都可以从 m 个不同的供应商处购得。设 wij 是从供应商 j 处购得的部件 i 的重量, cij 是相应的价格。 设计一个优先队列式分支限界法,给出总价格不超过 d 的最小重量机器设计。 对于给定的机器部件重量和机器部件价格,设计一个优先队列式分支限界法,计算总价格不超过 d 的最小重量机器设计。 数据输入: 第一行有 3 个正整数 n ,m 和 d。接下来的 2n 行,每行 m 个数。前 n 行是 c,后 n 行是 w。 Code(回溯法) #inclu

ccql's Blog 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

算法分析与设计——实验5:分支限界法

1、使用分支限界算法解决单源最短路径问题。 2、使用分支限界算法解决0-1背包问题。 3、在N*N的棋盘上放置彼此不受攻击的N个皇后,按照国际象棋的规则,皇后可以攻击与之处于同一行或同一列或同一斜线上的棋子。N皇后的问题等价于在N*N大小的棋盘中放置N个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。使用队列式分支限界法,求出N个皇后的一种放置方案。

阮阮的阮阮的博客 2754

《算法设计与分析》第五六章:回溯法与分支限界法

《算法设计与分析》(耿国华版)第五六章:回溯法与分支限界法

2203_75327677的博客 1250

算法设计与分析——分支限界法——n皇后问题

#include<iostream> #include<queue> #include<cmath> #include<vector> #include<fstream> #include<sstream> using namespace std; //定义一个节点类 struct Node{ int number; vector<int>x;//保存当前解 }; //定义一个Queen的类 class Q

qq_50675813的博客 6355

算法设计与分析第六章 分支限界法

分枝限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出使某一目标函数值达到极大或极小的解,即在某种意义下的最优解

qq_46664080的博客 4205
上一篇: 【整理】旅行商问题(traveling salesman problem,TSP)
下一篇: 【算法设计与分析qwl】04 NP-hard——只是举例几个,货郎,双机调度,01背包
羰基绳命
博客等级 码龄6年 239粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值