最短路小结 Floyd + Dijkstra + 带花费 + 字符节点
来源:互联网 发布:印度软件外包 编辑:程序博客网 时间:2024/05/22 05:19
Floyd
ps:有向图是带有方向的图,就是有箭头的图,此介绍以有向图为例,无向图不计方向。
初始工作把这张有向图用一个二维数组存储,第一个下表表示起点,第二个表示终点,数组值代表长度。
如下图:
自己到自己的距离为 0
从表中看的出,部分路径并不是最短,而且有些路径没有表示出来,所以我们需要更新这个二维数组。
那么如果两点之间不是最短距离,那么需要一个点来中专减少距离
比如:由 4 到 3,直接的话距离为 12,但是经过 节点1 的中转, 变为了 11 ,如果经过 1 和 2 的中转,变为了 10。
假设现在只允许通过 节点1 中转,假设现在从 i 到 j 点,那么长度为G[i][j], 如果经过 节点1 的话,那么长度就变为 G[i][1] + G[1][j]。
如果
for(int i = 1; i <= n ; i++) for(int j = 1; j <= n; j++) if(G[i][j] > G[i][1] + G[1][j]) G[i][j] = G[i][1] + G[1][j];
以此类推,把每个节点遍历一遍。
for(int k = 1; k <= n ; k++) for(int i = 1; i <= n ; i++) for(int j = 1; j <= n; j++) if(G[i][j] > G[i][k] + G[k][j]) G[i][j] = G[i][k] + G[k][j];
这时候可能会问,为什么从 4 到 3 为什么是 10,不是经过一个节点吗,应该是 11,但是在 1 到 3 的时候也用了这个方法,他选择了有节点的走法,然后到 4 和 3 的时候就判断走 不走1节点,1节点以后的长度已经最优。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544
参考代码
#include<iostream> #define inf 0x3f3f3f3f using namespace std;int G[10010][10010];int main () { int n, m; while(~scanf("%d %d", &n, &m) && n) { for(int i = 0; i <= n; i++) { for(int j = 0; j <= n; j++) { if(i == j) G[i][j] = G[j][i] = 0; else G[i][j] = G[j][i] = inf; } } int x, y, w; for(int i = 1; i <= m; i++) { cin >> x >> y >> w; if(G[x][y] > w) { G[x][y] = G[y][x] = w; } } for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(G[i][j] > G[i][k] + G[k][j]) G[i][j] = G[i][k] + G[k][j]; } } } cout << G[1][n] << endl; } return 0;}
下面介绍 Dijkstra
首先还是需要一个二维数组来记录
这次不在G数组中更新数值,而是创建一个 dis 数组,来记录长度。
我们设起点为 1 , 那么dis[1] = 0;(设谁为起点谁为0)
因为是求 1 到其他个点的长度,那么首先就是想找离 1 最近的点,那就是 2 点, 选择了2号顶点, 而且没有中转比它还小,因为 2 号点是里面最小的点了,没有谁能比它还小,而且还需要中转。
那么找到2号顶点之后,引申出两个点3 ,4 点
1 → 2 → 3, 和 1→3, 比较哪个更近, 写入dis[3],(起点默认为1)
换成代码就应该是
if(dis[3] > dis[2] + G[2][3]) dis[3] = dis[2] + G[2][3];
dis[3] = 12
dis[2] + G[2][3] = 1 + 9 = 10
所以dis[3] = 10
同理 2→4 的时候也是比较 dis[4] 和 dis[2] + G[2][4]
最后如图:
☆小总结
先说读取,把G数组也就是map,全部记为inf 无限大,然后读取数值,更改两点之间的长度。
dis数组初始为直接连点的长度,即图上显示直接连接的长度,如果没有就记为inf
vis数组初始先标记起点
更新dis数组:
1.找到离起点,即与0最近的点,
2.vis标记这个点
3.用这个点为中转站,判断到终点哪个更短
4.更新到dis数组中
这样dis存的点为起点到各个点的最短距离了
练习题目:和上面一样
ac代码:
#include<iostream> #include<cstring>#define inf 0x3f3f3f3f using namespace std;int n, m;int vis[10010];int dis[10010];int G[10010][10010];void dijkstra(int n) { for(int i = 1; i <= n; i++) { dis[i] = G[1][i]; } memset(vis, 0, sizeof(vis)); vis[1] = 1; for(int cnt = 1; cnt <= n-1; cnt++) { int temp = inf; int u; for(int i = 1; i <= n; i++) { if(!vis[i] && dis[i] < temp) { temp = dis[i]; u = i; } } if(temp == inf) break; vis[u] = 1; for(int i = 1; i <= n; i++) { if(dis[i] > dis[u] + G[u][i]) dis[i] = dis[u] + G[u][i]; } }}int main () { while(~scanf("%d %d", &n, &m) && n+m) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { if(i == j) G[i][j] = G[j][i] = 0; else G[i][j] = G[j][i] = inf; } } int a, b, c; for(int i = 1; i <= m; i++) { cin >> a >> b >> c; G[a][b] = G [b][a] = c; } dijkstra(n); printf("%d\n", dis[n]); } return 0;}
Dijkstra带有花费的最短最省
顾名思义就是每条路不仅有长度,而且有花费,优先选择路程最少的,如果一样的话,优先选择花费少的。
这里我们选择使用结构体来做
struct { int w, v;}s[1010][1010], dis[1010];
s代表地图, 即上题中的G数组,dis还是dis, w代表路程,v代表花费,初始的时候要初始化
void init(int n) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { s[i][j].w = s[i][j].v = inf; } } for(int i = 1; i <= n; i++) dis[i].v = dis[i].w = inf;}
初始化全部为inf ,即无限大
然后数据输入,这里有个判定,
当输入的起点终点一样
花费后来输入的小,那么就要修改花费值
路程后来输入的小,那么就要修改路程
for(int i = 1; i <= m; i++) { scanf("%d %d %d %d", &x, &y, &w, &v); if(s[x][y].w > w || (s[x][y].w == w && s[y][x].v > v)) { s[x][y].w = s[y][x].w = w; s[x][y].v = s[y][x].v = v; }}
在dijkstra函数内,更新dis数组的时候 除了dis[i].w > dis[u].w + s[u][i].w之外,还要修改当dis[i].w == dis[u].w + s[u][i].w 时的花费值,dis[i].w == dis[u].w + s[u][i].w && dis[i].v > dis[u].v + s[u][i].v
保证选择当路程一样的时候花费也是最小的
for(int i = 1; i <= n; i++) { if(dis[i].w > dis[u].w + s[u][i].w || (dis[i].w == dis[u].w + s[u][i].w && dis[i].v > dis[u].v + s[u][i].v)) { dis[i].w = dis[u].w + s[u][i].w; dis[i].v = dis[u].v + s[u][i].v; } }
题目链接 :http://acm.hdu.edu.cn/showproblem.php?pid=3790
AC代码:
#include<iostream>#include<cstring> #define inf 0x3f3f3f3f using namespace std;int vis[10010];struct { int w, v;}s[1010][1010], dis[1010];void init(int n) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { s[i][j].w = s[i][j].v = inf; } } for(int i = 1; i <= n; i++) dis[i].v = dis[i].w = inf;}void dijkstra(int st, int ed, int n) { dis[st].w = 0; dis[st].v = 0; memset(vis, 0, sizeof(vis)); for(int cnt = 1; cnt <= n; cnt++) { int minn = inf; int u = - 1; for(int i = 1; i <= n; i++) { if(vis[i] == 0 && dis[i].w < minn) { minn = dis[i].w; u = i; } } if(u == -1) break; vis[u] = 1; for(int i = 1; i <= n; i++) { if(dis[i].w > dis[u].w + s[u][i].w || (dis[i].w == dis[u].w + s[u][i].w && dis[i].v > dis[u].v + s[u][i].v)) { dis[i].w = dis[u].w + s[u][i].w; dis[i].v = dis[u].v + s[u][i].v; } } }}int main () { int n, m; while(~scanf("%d %d", &n, &m) && n+m) { init(n); int x, y, w, v; for(int i = 1; i <= m; i++) { scanf("%d %d %d %d", &x, &y, &w, &v); if(s[x][y].w > w || (s[x][y].w == w && s[y][x].v > v)) { s[x][y].w = s[y][x].w = w; s[x][y].v = s[y][x].v = v; } } int st, ed; scanf("%d %d", &st, &ed); dijkstra(st, ed, n); printf("%d %d\n", dis[ed].w, dis[ed].v); } return 0;}
名字为字母的最短路
当输入并不是1234点的时候,我们需要把它归结于数字,然后用通常的方法去解;
这里使用的是STL中的map
定义为
map<string, int> cnt;
利用map的功能,每次可以从map中查询有没有出现过这个地名,如果没有,那么附一个数值kase给这个地名,然后kase++,保证每次赋的值是不同的,cnt.count(s1) 的意思是在cnt中查询有没有出现过s1字符串,如果没有返回非,如果有返回是,一旦把这个工作做完,cnt[s1]就代表一个数字,就如前面题目中的1234,把地名转换为数字来求解
for(int i = 0; i < n; i++) { cin >> s1 >> s2 >> x; if(!cnt.count(s1)) cnt[s1] = ++t; if(!cnt.count(s2)) cnt[s2] = ++t; G[cnt[s2]][cnt[s1]] = G[cnt[s1]][cnt[s2]] = min(x, G[cnt[s1]][cnt[s2]]); }
练习题目 :http://acm.hdu.edu.cn/showproblem.php?pid=3790
AC代码 :
#include<map>#include<iostream>#include<string>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int inf = 0x3f3f3f3f;int G[155][155], vis[155], dis[155];map<string, int> cnt;int t, n;int dijkstra(int S,int T) { int u; memset(vis,0,sizeof(vis)); for(int i = 0; i <= t; i++) dis[i] = G[S][i]; dis[S] = 0; vis[S] = 1; for(int i = 1; i < n; i++) { int temp = inf; for(int j = 0; j <= t; j++) { if(!vis[j] && dis[j] < temp) { temp = dis[j]; u = j; } } if(temp == inf) continue; vis[u] = true; for(int j = 0; j <= t; j++) { if(!vis[j] && dis[j] > dis[u] + G[u][j]) dis[j] = dis[u] + G[u][j]; } } return dis[T];}int main() { int S, T, x; string s1, s2; while(scanf("%d", &n) && n != -1) { t = -1; cnt.clear(); memset(G, inf, sizeof(G)); cin >> s1 >> s2; cnt[s1] = ++t; S = t; if(!cnt.count(s2)) cnt[s2] = ++t; T = t; for(int i = 0; i < n; i++) { cin >> s1 >> s2 >> x; if(!cnt.count(s1)) cnt[s1] = ++t; if(!cnt.count(s2)) cnt[s2] = ++t; G[cnt[s2]][cnt[s1]] = G[cnt[s1]][cnt[s2]] = min(x, G[cnt[s1]][cnt[s2]]); } int ans = dijkstra(S, T); if(ans == inf) printf("-1\n"); else printf("%d\n", ans); } return 0;}
- 最短路小结 Floyd + Dijkstra + 带花费 + 字符节点
- 最短路dijkstra,floyd
- 最短路Floyd、Dijkstra
- 15th 【最短路 dijkstra】最小花费
- hdu2544最短路(floyd)(dijkstra)
- 最短路 spfa, dijkstra, Floyd
- 最短路模版 【dijkstra】【floyd】
- 最短路【dijkstra】【floyd 】【spfa】
- poj1847Tram 最短路 floyd dijkstra
- hdu 2544 最短路 (dijkstra/floyd/spfa)
- HDU 2544 最短路 dijkstra floyd
- [dijkstra/SPFA/floyd]HDU 2544最短路
- hdu2544 最短路(Floyd,Dijkstra算法)
- 【最短路三算法】Floyd,Dijkstra,SPFA.
- 最短路 Dijkstra 和 Floyd 算法
- POJ2253 Frogger -DIJKSTRA || FLOYD最短路练习
- 最短路:Dijkstra算法和Floyd算法
- 最短路模板 Dijkstra+Floyd+SPFA
- 注解@知识总结
- PA 测试注意事项
- tensorflow tf.logical_and(True, False) tf.logical_not(x, name=None)
- 基于pyqt5+python3.4的cups每日商户统计程序开发
- Redis探索之旅(5)- 在Linux下将Redis注册成服务
- 最短路小结 Floyd + Dijkstra + 带花费 + 字符节点
- 外部数据查询整合投产过程 日记一则
- ubuntu版网易云音乐下载
- leetcode 278. First Bad Version
- Check failed: result == ncclSuccess (2 vs. 0) system error
- 美素数
- tomcat(web服务器)加载前端代码和后台代码原理
- Windows下sublime text3 配置python开发环境
- Spring redis key乱码