图论

  • 王道论坛机考系列——图论之最短距离
    • 最短距离——Floyd算法(解决全源)
      • 最短路——Floyd算法
      • 最短路——Dijkstra算法一
      • 最短路径问题——Dijkstra算法二

王道论坛机考系列——图论之最短距离

最短距离——Floyd算法(解决全源)

寻找图中某两个特定节点之间的最短路径的长度。
图使用邻接矩阵edge[i][j]表示,edge[i][j]最初始的时候表示的是从节点i不经过任何节点到节点j的距离,如果不存在则设置为无穷大。那么从节点i开始可以经过节点l再到节点j,此时需要比较edge[i][l]+edge[l]][j]与edge[i][j]的大小,如果前者比较小,那么edge[i][j]的值需要更换一下(其中edge[i][l]表示从i到l不经过任何节点的距离,edge[l][j]表示从l到j不经过任何中间节点的距离),换做更一般的情况,当从i到j的中间节点数量增多时,可以使用ans[k][i][j]表示从节点i开始只经过小于等于k的节点到达j节点时的最短路径。计算的过程只需要从k等于1到n计算图中所有节点i到节点j之间的距离, ans[0][i][j]表示从i到j的直接距离。

使`cpp描述短发可以表示如下所示:

for (int k = 1; k <= n; k++) {//  计算只经过小于等于k的节点到达j节点时的最短路径for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {//  遍历所有的i,jif (ans[k-1][i][k] == INT_MAX || ans[k-1][k][j] == INT_MAX) {//  表示经过前k-1个节点时,i或者j不与k相通ans[k][i][j] = ans[k-1][i][j];// 保持原值,即从i到j经过k个节点和经过k-1个节点最终得到的结果是一样的continue;  //继续执行程序}if (ans[k-1][i][j] == INT_MAX ||ans[k-1][i][k] + ans[k-1][k][j] < ans[k-1][i][j])ans[k][i][j] = ans[k-1][i][k] + ans[k-1][k][j];elseans[k][i][j] = ans[k-1][i][j];}}
}

上面的方法中使用ans[k-1][i][j]的值来推导得到ans[k][i][j]的值时,值得注意的是ans[k][i][k] 和 ans[k][k][j]的值与 ans[k-1][i][k]和 ans[k-1][k][j]的值相同。所以程序更新为以下的内容,程序的运行结果与上面的运行结果是一样的,成功的将需要使用三位数组来解决的问题变成了只需要使用二维数组就可以解决的问题:

for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if(ans[i][k] == INT_MAX || ans[k][j] == INT_MAX)continue;if (ans[i][j] == INT_MAX || ans[i][k] + ans[k][j] < ans[i][j])ans[i][j] = ans[i][k] + ans[k][j];}}
}

最短路——Floyd算法

输入:包含多组数据,每组数据的第一行是两个整数N和M(N <= 100, M <= 10000),N表示节点的个数,M则表示图中存在M条边,接下来输入有M行,每一行包含三个数A, B, C(1 <= A,B <= 100, C <= 1000)A, B表示节点的编号,C表示节点A到节点B的长度,当输入N与M均为0时,表示结束。
输出:对于每一组输入,输出一行,表示从节点1到节点N的最短路径。

样例输入:
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
#include <iostream>
#include <limits.h>
#define NUM 1001
using namespace std;int arr[NUM][NUM];  //  用来表示图的矩阵void FloydMinPath(int ans[][NUM], int n) {for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <+ n; j++) {if (ans[i][k] == INT_MAX || ans[k][j] == INT_MAX)continue;if (arr[i][j] == INT_MAX || arr[i][k] + arr[k][j] < arr[i][j])arr[i][j] = arr[i][k] + arr[k][j];}}}
}int main() {int n, m;while(scanf("%d%d", &n, &m) != EOF) {if(n > 0 && m > 0) {for (int i = 0; i < NUM; i++)for (int j = 0; j < NUM; j++)arr[i][j] = INT_MAX;  //  初始化矩阵int a, b, c;for (int i = 0; i < m; i++) {scanf("%d%d%d", &a, &b, &c);arr[a][b] = c;arr[b][a] = c;   //  这里需要特别注意,我第一次的时候就忘记了}FloydMinPath(arr, n);printf("%d\n", arr[1][n]);} else {break;}}return 0;
}

弗洛伊德算法的时间复杂度是O(n3),空间复杂度是O(n2),并且弗洛伊德算法不适合于节点之间的距离为负数的情况。

最短路——Dijkstra算法一

本道题目使用Dijkstra算法计算从节点1到其余所有节点的最短路径。

#include <stdio.h>
#include <vector>
#include <limits.h>
#define NUM 101
using namespace std;struct Edge {  //  邻接链表中的链表元素结构体int next;  //  一条边上的另一个顶点int len;   //  边的长度
};vector<Edge> edge[NUM];bool mark[NUM];  //  标记节点是否被加入集合中
int dis[NUM];    //  记录从节点1到其余所有节点的距离void Dijkstra(int n, int root) {for (int i = 0; i <= n; i++) {//  初始化节点和距离mark[i] = false;dis[i] = -1;}mark[root] = true;  //  将源节点加入到K集合中;dis[root] = 0;      //  节点自己到自己的距离为0;int newP = root;for (int k = 1; k < n; k++) {//  进行n-1此循环,从节点root开始,按照最短路径递增的顺序//  每次添加一个新的节点;for (int j = 0; j < edge[newP].size(); j++) {//  查找顶点newP的所有相邻的边int next = edge[newP][j].next;int len = edge[newP][j].len;if (mark[next]) continue;  //  如果已经在K集合里面就不用管了if (dis[next] == -1 || dis[next] > len + dis[newP])dis[next] = dis[newP] + len;}// 查找最小的边;int min = INT_MAX;for (int i = 1; i <= n; i++) {if (mark[i]) continue;if (dis[i] == -1) continue;  // 表示不可达if (dis[i] < min) {min = dis[i];newP = i;}}//  最短路径的另一个顶点作为下一次的起点mark[newP] = true;}
}int main() {int n, m;while(scanf("%d%d", &n, &m) != EOF) {if (n == 0 && m == 0) break;int a, b, c;while(m--) {scanf("%d%d%d", &a, &b, &c);Edge e;e.next = b;e.len = c;edge[a].push_back(e);e.next = a;edge[b].push_back(e);}Dijkstra(n, 1);printf("%d\n", dis[n]);}return 0;
}

以上的实现思路是借助王道机试指南上的做法的,第一次自己实现失败是因为没有正确理解Dijkstra算法,再加上对于每一个顶点的边的处理不是很恰当,以上的做法使用结构体数组来保存,对于每一个节点保存下它的相邻的边,在查找节点的边的时候就会很便捷,这种思路对于个人来说还是比较新奇的。

最短路径问题——Dijkstra算法二

题目描述:

给你n个点m条无向边,每条边都有长度d和花费p,给你起点s和终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。

输入:输入n,m,点的编号是1~n,然后是m行,每行四个数a, b,d, p,表示a,b之间有一条边,且其长度为d,花费为p。最后一行是两个数s,t。起点s,终点t。n和m为0时输入结束。(1 < n <= 1000, 0 < m < 100000, s != t)。

输出:输出一行两个数,最短距离及其花费。

样例输入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出:
9 11
#include <stdio.h>
#include <vector>
#include <limits.h>
#define NUM 101
using namespace std;struct Edge {  //  邻接链表中的链表元素结构体int next;  //  一条边上的另一个顶点int len;   //  边的长度int cost;  //  代价
};vector<Edge> edge[NUM];bool mark[NUM];  //  标记节点是否被加入集合中
int dis[NUM];    //  记录从开始节点到其余所有节点的距离
int cost[NUM];   //  记录从开始节点到其余所有节点的代价void Dijkstra(int n, int start) {for (int i = 0; i <= n; i++) {mark[i] = false;dis[i] = -1;cost[i] = -1;}int newP = start;mark[newP] = true;dis[newP] = 0;cost[newP] = 0;for (int k = 1; k < n; k++) {//  从起始节点开始,以最短距离递增的规律每次增加一个节点for (int i = 0; i < edge[newP].size(); i++) {//  查找节点的相邻边int ne = edge[newP][i].next;int le = edge[newP][i].len;int co = edge[newP][i].cost;if (mark[ne]) continue;//  if (dis[ne] == -1 || dis[ne] > dis[newP] +le) {//  上面的内容是原版的内容if (dis[ne] == -1 || dis[ne] > dis[newP] + le ||(dis[ne] == dis[newP] + le && cost[ne] > cost[newP] + co)) {//  更新的时候将距离相同单花费更少的也作为更新的条件之一dis[ne] = dis[newP] + le;cost[ne] = cost[newP] + co;}}//  寻找最小的节点int min = INT_MAX;int cost_ = INT_MIN;for (int i = 1; i <= n; i++) {if (mark[i]) continue;if (dis[i] == -1) continue;  // 不可达的点跳过// if (dis[i] < min || (min == dis[i] && cost_ < cost[i])) {//  上面的内容是我在处理距离相同时,花费小优先所做的处理,但是参考程序是在40行//  判断那里做了一定的修改if (dis[i] < min) {min = dis[i];cost_ = cost[i];newP = i;}}mark[newP] = true;}
}int main() {int n, m;while(scanf("%d%d", &n, &m) != EOF) {if (n == 0 && m == 0) break;int a, b, d, p;while(m--) {scanf("%d%d%d%d", &a, &b, &d, &p);Edge E;E.next = b;E.len = d;E.cost = p;edge[a].push_back(E);E.next = a;edge[b].push_back(E);}int s, t;scanf("%d%d", &s, &t);Dijkstra(n, s);printf("%d %d\n", dis[t], cost[t]);}return 0;
}

王道论坛机考系列——图论之最短距离相关推荐

  1. 王道论坛机试指南学习笔记(四)图论

    文章目录 1. 并查集 - 基本表示 - 集合的合并 - 查找根节点 - 查找连通分量个数 2. 最小生成树MST - 定义 - Kruskal算法 - java代码 3. 最短路径 - 定义 - F ...

  2. 考研机试准备--《王道论坛机试指南》学习笔记

    一.代码能力培养的层次结构 1.会编写(默写)经典程序的代码. 2.将自己的想法和抽象的问题转换为代码实现. 3.编写出的代码在大量的,多种多样的测试用例之前仍然具有健壮性. 二.常见概念 1.特殊判 ...

  3. 王道论坛机试指南学习笔记(五)搜索

    文章目录 1. 枚举 - 简述 - 百鸡问题 2.2 BFS - 算法 - 迷宫问题 - 可乐问题 - 解题关键 3. 递归 - 思想 - 汉诺塔 - 素数环问题 - 图遍历 4. DFS - 算法 ...

  4. 王道论坛机试指南学习笔记(一)经典入门

    文章目录 1. 排序 2.1 快速排序 - 思想 - 复杂度 - 代码 2. 日期问题 2.1 数据结构 2.2 判断闰年 2.3 获取下一天 2.4 计算间隔日期 3. HashMap 3.1 内部 ...

  5. 王道论坛机试指南学习笔记(二)数据结构

    文章目录 1. 栈 - 基本用法 - 括号匹配 - 四则运算器 2. 优先队列 - 基本用法 - 哈夫曼树 3. 二叉树 - 数据结构 - 前序遍历 - 中序遍历 - 后序遍历 - 根据前中序生成二叉 ...

  6. 王道论坛机试指南学习笔记(三)数学问题

    文章目录 1. 2的n次方 - 移位 - power 2. 特殊值 - 最大值 - 绝对值 3. 取整 4. 数位拆解 - 数学方法 - 代码方法 5. 进制转换 - n转10 - 10转n 6. G ...

  7. 王道出版的机试指南_《王道论坛计算机考研机试指南》试读版.pdf

    <王道论坛计算机考研机试指南>试读版 王道论坛 王道论坛计算机考研机试指南 王道论坛 2013.01.06 写在前面的话 各位王道的小崽子们,今天你们考完初试了,感觉解放了吧?轻松了吧?无 ...

  8. 哈工大计算机研究生复试笔试,2011年哈工大计算机研究生复试(机考+面试)周

    好久没来这儿了,主要是上周是我们哈尔滨工业大学计算机科学与技术学院研究生复试和我们毕业开题答辩的时间,好多好多事-- 昨天晚上获得了研究生的特等奖学金的同学请全班吃的烤肉,现在身上还有味道,也多亏了有 ...

  9. 中国科学技术大学 2014 计算机 复试方案,中科大2014复试经验 - 中国科学技术大学 - 王道论坛,专注于计算机考研的点点滴滴! - Powered by Discuz!...

    本帖最后由 Tsinghua_2013 于 2014-4-7 22:08 编辑 中科大2014复试经验 谨以此文献给在考研过程中给过我资料.经验.希望.力量的王道论坛,希望王道能越办越好.并希望此文能 ...

最新文章

  1. std::string用法总结
  2. (Prototype)原型模式的Java实现(转)
  3. springboot(2.2.4)全局异常处理(RestControllerAdvice,ExceptionHandler)
  4. JavaScript中替换字符串中的所有小数点
  5. POJ 1745 Divisibility DP
  6. 程序员,活得是本事:30 岁后的 人生建议
  7. 商汤科技回应“IPO推迟”:“被”IPO,还“被”推迟了
  8. 编译的警告:隐式声明与内建函数 ‘malloc’ 不兼容
  9. 一个人有没有大数据思维,主要体现在哪两个方面?
  10. MAPGIS做图指导
  11. JS学习——贪吃蛇代码(简易版)
  12. Final shell配置
  13. 计算机上发出打印命令,从计算机上发送打印命令后打印机不打印怎么办
  14. RuoYi若依打包发布与部署
  15. Bootstrap 4 snippets 代码段
  16. logback系列之四:输出日志到不同文件
  17. 绘制蛋白-配体2D相互作用工具汇总
  18. JavaScript:JavaScript这些小技巧你必须要知道
  19. 2022-2028年中国人力资源服务行业市场发展前景及投资风险评估报告
  20. ABP zero 4.2 发布

热门文章

  1. 开发人员总是犯一些低级错误怎么解决?
  2. [OpenCV实战]49 对极几何与立体视觉初探
  3. 智商情商哪个重要_情商与智商哪个更重要
  4. 安卓4.2版本以上时连接HC06蓝牙模组失败的问题及其解决方案
  5. 无盘服务器配置2018,遥志CCBoot无盘软件
  6. [转] WEB设计趋势与潮流
  7. 树莓派添加tft/oled显示屏
  8. verilog程序,ISE 10.1环境下,检查语法和仿真均可,综合出错“ this signal is connected to multiple drivers.”
  9. java中M格式_Java中Date日期字符串格式的各种转换
  10. C语言printf重入,单片机中printf函数的重映射