03
priority queue实现
接下来是优先队列式分枝限界法,我们以单源最短路径问题为例。
最短路径依旧是一个熟悉的问题,可以通过过去的推文了解,就不多解释了:
先简单介绍一下优先队列:
优先队列可以分为最大、最小优先队列。相比于先进先出的普通队列,优先队列每次都是最大(或最小)的元素优先出队。
在单源最短路径问题中,我们采用的剪枝函数则类似于之前提到的Dijkstra算法(其实它本身也就是源于BFS),通过寻找当前离原点最近的点进行下一步操作;通过松弛操作得出下一层子节点。说是最优优先出队,其实这里也只有最小点一个出队拓展新子点了。之前推文里有提到具体算法,可以参考一下,就不难理解了。
这里暂时不深入讲解如何实现优先队列,而是直接通过头文件调用。由于我也不是很习惯用优先队列,这里参考了网上的代码。具体讲解参见注释:
Code:
//优先队列式分支限界法 解单源最短路径问题 //来源互联网 ,作者csdn zzzsdust 原文链接见后文 #include <bits/stdc++.h> //头文件中附带有优先队列 using namespace std; class MinHeapNode //最小堆 { public: int id; int length; //从起始点 v 到点 id 的距离 public: friend bool operator < (const MinHeapNode &a, const MinHeapNode &b) //运算符重载 { return a.length < b.length; } friend bool operator > (const MinHeapNode &a, const MinHeapNode &b) { return a.length > b.length; } }; const int max_ = 0x3f3f3f; int Graph[100][100]; //输入两点间距离 int dist[100]; //到原点距离 int pre[100]; //记录最短路径中的前一个点 int n, m, v; void OutPutPath(int i) //输出到原点的最短路径 { if(i == pre[i]) { printf("%d", i); return; } else { OutPutPath(pre[i]); printf(" %d", i); } } void OutPut() { for(int i = 1; i <= n; ++i) { if(i != v) { printf("点 %d 到 %d 的最短距离是 %d\n", v, i, dist[i]); printf("路径为:"); OutPutPath(i); printf("\n"); } } } //划重点!! void ShortestPaths() { priority_queue<MinHeapNode, vector<MinHeapNode>, greater<MinHeapNode> > q; /*调用优先队列 , 第一个参数是数值类型; 第二个参数是存放数值的容器类型,一般用vector; 第三个参数为排序规则。 greater升序,即小的先出;less降序,即大的先出。 具体函数: 1.插入 .push()函数 2.取出顶端元素 .top()函数 3.删除顶端元素 .pop()函数 4.大小 .size()函数 5.是否为空 .empty()函数 */ memset(dist, max_, sizeof(dist)); //初始化距离 dist[v] = 0; pre[v] = v; MinHeapNode cur_p; cur_p.id = v; cur_p.length = 0; q.push(cur_p); while(true) { if(q.empty() == 1) //全员松弛完毕 break; cur_p = q.top(); //取出堆顶的点(距离最短) q.pop(); // 在优先队列中删除刚取出的点 for(int i = 1; i <= n; ++i) { if(Graph[cur_p.id][i] != max_ && (cur_p.length + Graph[cur_p.id][i] < dist[i])) //剪枝函数,也就是Dijkstra中的松弛 { dist[i] = cur_p.length + Graph[cur_p.id][i]; pre[i] = cur_p.id; MinHeapNode temp; temp.id = i; temp.length = dist[i]; q.push(temp); } } } } void InPut() //输入数据 { int x, y, len; scanf("%d %d %d", &v, &n, &m); //以v为原点,n个点,m条边。 memset(Graph, max_, sizeof(Graph)); //默认设置为最大,表示无法到达 for(int i = 1; i <= m; ++i) { scanf("%d %d %d", &x, &y, &len); Graph[x][y] = Graph[y][x] = len; //无向图!! } } int main() { InPut(); ShortestPaths(); OutPut(); }
Reference:
https://blog.csdn.net/qjt19950610/article/details/89476784
https://blog.csdn.net/m0_38015368/article/details/80449014
《Introduction to The Design and Analysis of Algorithms》
by Anany Levitin 潘彦 译
后记:
分枝限界法最有名的作用还是求解整数规划问题。
这部分问题我们暂且搁一会儿,等未来再讲。
(关键看我什么时候编出正确的代码)
其实到这里,一个部分的内容也结束了。
这一部分主要是讲算法设计思想。
然而rookie的我连数据结构的不过关。。。
因此打算接下来一段时间恶补一波。
可能会写相关的内容。敬请期待~~