C/C++分支限界法求多段图最短路径问题

多段图的最短路径问题是求原点到终点的最小代价路径

分支限界法求解步骤:
1.确定合理的限界函数,根据限界函数确定目标函数的界[down,up]
2.从起点开始,按照广度优先策略搜索问题的解空间树,将在界范围内的节点加入PT表
3.从PT表中取出节点做拓展节点,重复第二步,直到找出最优解

下面我们引入一个实际例子:
在这里插入图片描述
根据求解步骤进行分析:
1.由此图我们容易想到一个简单的下界,从起点出发取两段之间路径最小值,直到到达终点,即down=2+4+5+3=14。通常我们可以使用贪心法求上界,即up=2+6+6+3=17。
2.结合求上下界的方法,我们可以得到一个目标函数:
lb=走过路径长度+该节点到下一段的最小距离+剩余各段之间的最小距离之和。
3.由于走到第n-1段(假设有n段)目标函数值等于按该节点路径从起点走到终点的最短路径,因此,若此时目标函数为所有目标函数中的最小值,则该目标函数值即为最优解。

图解过程为:
在这里插入图片描述
完整代码:

#include<stdio.h>
#include<iostream>
#include<queue>     //C++优先队列头文件 
using namespace std;
#include<malloc.h>
#define INF 1000
const int maxNum_v=20;   //最大节点数 
const int maxNum_p=5;    //最大段数 
int down=0;              //下界 
int up=0;               //上界 
int n;                  //实际节点数 
int p_num;              //实际段数 
typedef struct Node1
{
	int nv;   //当前节点 
	int path;     //走过路径 
	int lb;      //目标函数值 
	int p;     //段号 
	bool operator < (const Node1 &x)const
	{
		return x.lb<lb;              //小根堆优先队列 
	}
}Node1;
typedef struct Node2     
{
	int v;
	Node2 *next;               
}Node2;
typedef struct Node
{
	int v;
	int wight;
	Node *next;
}Node; 
Node *p[maxNum_v];     //存储边的邻接链表 
Node2 *pv[maxNum_p];    //存储段的邻接链表 
priority_queue<Node1> pq;
void creat()                   //建立上面两个邻接链表 
{
	printf("请输入节点数:");
	scanf("%d",&n);
    int queue[n];   //用队列依次录入每个节点 
	int front=0;
	int tail=0;
	queue[tail++]=0;
	bool visited[n];   //设置访问标签,避免多个节点指向一个节点,后一个节点被重复录入的情况 
	for(int i=0;i<n;i++)
	{
		visited[i]=false;
	}
	visited[0]=true;
	while(front!=tail)
	{
	    p[queue[front]]=(Node *)malloc(sizeof(Node));
		p[queue[front]]->next=NULL;
		int num;
		printf("请输入节点 %d 指向节点的数目:",queue[front++]);
		scanf("%d",&num);
		for(int j=0;j<num;j++)
		{
			Node *tmp;
			tmp=(Node *)malloc(sizeof(Node));
		printf("请输入 %d 节点指向节点和权值,格式如 2 3:",queue[front-1]);
		scanf("%d %d",&tmp->v,&tmp->wight);
		if(!visited[tmp->v])
		{
		queue[tail++]=tmp->v;
		visited[tmp->v]=true;
		} 
		tmp->next=p[queue[front-1]]->next;
		p[queue[front-1]]->next=tmp;
		getchar();
		}
	}
	printf("请输入该多段图的段数:");
	scanf("%d",&p_num);
	for(int i=0;i<p_num;i++)
     {
        pv[i]=(Node2*)malloc(sizeof(Node2));
        pv[i]->next=NULL;
     	int v_num; 
     	printf("请输入第 %d 段的节点数:",i);
     	scanf("%d",&v_num);
     	printf("请输入节点(形如 1 2 3):");
     	for(int j=0;j<v_num;j++)
     	{
     		Node2 *tmp;
     		tmp=(Node2*)malloc(sizeof(Node2));
     		scanf("%d",&tmp->v);
     		tmp->next=pv[i]->next;
     		pv[i]->next=tmp;
		 }
	 }	 
}
void get_down()      //获取下界函数 
{
	for(int i=0;i<p_num;i++)
	{
	    Node2 *q=pv[i]->next;
	    int min=INF;
	    while(q!=NULL)
	    {
	    	Node *pp=p[q->v]->next;
	    	if(pp==NULL)
	    	{
	    		min=0;
	    		break;
			}
	    	while(pp!=NULL)
	    	{
	    		if(pp->wight<min)
	    		min=pp->wight;
	    		pp=pp->next;
			}
	    	q=q->next;
		}
		down+=min;
	}
}
void get_up()   //获取上界函数 
{
   int start=0;
   Node *q;
   while(1)
   {
   		int min=INF;
   	q=p[start]->next;
   	if(q==NULL)
   	break;
   	while(q!=NULL)
   	{
   		if(q->wight<min)
   		{
   			min=q->wight;
   			start=q->v;
		   }
		   q=q->next;
	   }
	   up+=min;
   }
}
int get_lb(Node1 Node1)       //计算目标函数值 
{
	int min=INF;
	Node *q=p[Node1.nv]->next;
	if(q==NULL)
	return Node1.path;
	while(q!=NULL)
	{
		if(q->wight<min)
		min=q->wight;
		q=q->next;
	}
	Node1.lb=Node1.path+min;
//	printf("%d %d\n",Node1.path,min);
	Node2 *pp;
	for(int i=Node1.p+1;i<p_num-1;i++)   //查找未经过段到下一段的最小值 
	{
  	    min=INF;
		pp=pv[i]->next;    //段内节点 
	if(pp==NULL)
	  break;
	while(pp!=NULL)
	{
		Node *qq=p[pp->v]->next;  //根据节点找边 
		while(qq!=NULL)
		{
			if(qq->wight<min)
			min=qq->wight;
			qq=qq->next;
		}
		pp=pp->next;
	}
	Node1.lb+=min;
	}
	return Node1.lb;
}
int solve()
{
	get_down();
	get_up(); 
	Node1 first;   //初始化起点节点0 
	first.nv=0;
	first.p=0;
	first.path=0;
	first.lb=down;
    pq.push(first);
    int ret=INF;
    while(pq.size())
    {
    	Node1 tmp=pq.top();
    	pq.pop();
    	if(tmp.p==p_num-2)    //此时目标函数值等于走过路径大小,最小目标函数即为最优解 
	{
	      ret=tmp.lb;      
	      break;
	 } 
	 Node1 next;
	 Node *q=p[tmp.nv]->next;
	 while(q!=NULL)
	 {
	 	next.nv=q->v;
	 	next.p=tmp.p+1;
	 	next.path=tmp.path+q->wight;
	 	next.lb=get_lb(next);
	 //	printf("%d %d %d %d\n",next.lb,next.nv,tmp.path,q->wight);
	 	if(next.lb<=up)     //小于上界的节点入队列 
	 	pq.push(next);
	 	q=q->next;
	 }
	}	
	return ret;
 } 
main()
{
	creat();   
	printf("节点 节点 权值:\n");   //生成图信息 
	for(int i=0;i<n;i++)
	{
		Node *q=p[i]->next;
		while(q!=NULL)
		{
			printf("%d %d %d\n",i,q->v,q->wight);
			q=q->next;
		}
	 } 
	 printf("段 节点:\n"); 
	 for(int i=0;i<p_num;i++)
	 {
	 	Node2 *q=pv[i]->next;
	 	while(q!=NULL)
	 	{
	 		printf("%d %d\n",i,q->v);
	 		q=q->next;
		 }
	 }  
	 printf("最优解为:%d\n",solve());
//	 get_down();
//	printf("%d\n",down);   
   // get_up();
   // printf("%d\n",up);
}

运行结果:
录入阶段:
1.节点的邻接链表
输入
2.段的邻接链表
在这里插入图片描述
数据输出阶段:
1.检查图信息
在这里插入图片描述
2.最终结果
在这里插入图片描述

  • 12
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是用C++语言实现分支限界法解决单源最短路径问题的基本思路和代码: (1)定义一个结构体表示状态节点,包含节点编号和到该节点的路径长度。 ```c++ struct Node { int id; // 节点编号 int dist; // 到该节点的路径长度 bool operator<(const Node& other) const { return dist > other.dist; // 优先队列需要重载小于运算符 } }; ``` (2)定义一个优先队列用来存储未被扩展的状态节点,并按照路径长度从小到大排序。 ```c++ priority_queue<Node> pq; ``` (3)定义一个二维数组存储中节点之间的边权。 ```c++ const int MAXN = 1000; // 节点个数的最大值 int graph[MAXN][MAXN]; // 的邻接矩阵表示 ``` (4)定义一个一维数组存储从起点到各个节点的最短路径长度。 ```c++ const int INF = 0x3f3f3f3f; // 定义无穷大 int dist[MAXN]; // 存储起点到各个节点的最短路径长度 ``` (5)定义一个函数用来解决单源最短路径问题,其参数包括起点编号和节点个数。 ```c++ void dijkstra(int start, int n) { memset(dist, INF, sizeof(dist)); // 初始化起点到各个节点的路径长度为无穷大 dist[start] = 0; // 起点到自身的路径长度为0 pq.push({start, 0}); // 将起点加入优先队列 while (!pq.empty()) { // 当优先队列不为空时 Node node = pq.top(); // 取出路径长度最小的节点 pq.pop(); // 弹出该节点 int id = node.id, d = node.dist; // 取出节点编号和路径长度 if (d > dist[id]) continue; // 如果取出的节点已经不是最短路径,则跳过 for (int i = 0; i < n; ++i) { // 遍历与该节点相邻的所有节点 if (graph[id][i] != INF && d + graph[id][i] < dist[i]) { // 如果存在更短的路径,则更新路径长度 dist[i] = d + graph[id][i]; pq.push({i, dist[i]}); // 将更新后的节点加入优先队列 } } } } ``` 以上代码实现了单源最短路径问题的求解,其中用到了优先队列和邻接矩阵的数据结构。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值