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

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

下面我们引入一个实际例子:

多段图最短路径动态规划Python 多段图的最短路径c语言_c++


根据求解步骤进行分析:

1.由此图我们容易想到一个简单的下界,从起点出发取两段之间路径最小值,直到到达终点,即down=2+4+5+3=14。通常我们可以使用贪心法求上界,即up=2+6+6+3=17。

2.结合求上下界的方法,我们可以得到一个目标函数:

lb=走过路径长度+该节点到下一段的最小距离+剩余各段之间的最小距离之和。

3.由于走到第n-1段(假设有n段)目标函数值等于按该节点路径从起点走到终点的最短路径,因此,若此时目标函数为所有目标函数中的最小值,则该目标函数值即为最优解。图解过程为:

多段图最短路径动态规划Python 多段图的最短路径c语言_c++_02


完整代码:

#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.节点的邻接链表

多段图最短路径动态规划Python 多段图的最短路径c语言_链表_03


2.段的邻接链表

多段图最短路径动态规划Python 多段图的最短路径c语言_多段图最短路径动态规划Python_04


数据输出阶段:

1.检查图信息

多段图最短路径动态规划Python 多段图的最短路径c语言_链表_05


2.最终结果

多段图最短路径动态规划Python 多段图的最短路径c语言_i++_06