最短路小结 Floyd + Dijkstra + 带花费 + 字符节点
2017-07-21 15:42
197 查看
Floyd
ps:有向图是带有方向的图,就是有箭头的图,此介绍以有向图为例,无向图不计方向。
初始工作把这张有向图用一个二维数组存储,第一个下表表示起点,第二个表示终点,数组值代表长度。
如下图:
1 | 2 | 3 | 4 | |
---|---|---|---|---|
1 | 0 | 2 | 6 | 4 |
2 | ∞ | 0 | 3 | ∞ |
3 | 7 | ∞ | 0 | 1 |
4 | 5 | ∞ | 12 | 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];
1 | 2 | 3 | 4 | |
---|---|---|---|---|
1 | 0 | 2 | 5 | 4 |
2 | 9 | 0 | 3 | 4 |
3 | 6 | 8 | 0 | 1 |
4 | 5 | 7 | 10 | 0 |
题目链接: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] << endl; } return 0; }
下面介绍 Dijkstra
首先还是需要一个二维数组来记录
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | 0 | 1 | 12 | ∞ | ∞ | ∞ |
2 | ∞ | 0 | 9 | ∞ | ∞ | ∞ |
3 | ∞ | ∞ | 0 | ∞ | 5 | ∞|
4 | ∞ | ∞ | 4 | 0 | 13 | 14 |
5 | ∞ | ∞ | ∞ | ∞ | 0 | 4 |
6 | ∞ | ∞ | ∞ | ∞ | ∞ | 0 |
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
dis | 0 | 1 | 12 | ∞ | ∞ | ∞ |
因为是求 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]
最后如图:
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
dis | 0 | 1 | 8 | 4 | 13 | 17 |
☆小总结
先说读取,把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 ); } 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;
}
相关文章推荐
- HDU2112-HDU Today-最短路(dijkstra+字符转换)
- 15th 【最短路 dijkstra】最小花费
- Dijkstra 模板 最短路
- 图上最短路(Dijkstra, spfa)
- POJ 2449 第k短路 Dijkstra+A*
- POJ-1502-MPI Maelstrom [最短路][Dijkstra]
- HDU 5521 Meeting(虚拟节点+最短路)
- hdu 2544 最短路(Dijkstra模板)
- PHP中字符安全过滤函数使用小结
- linux字符设备驱动总结之:全自动创建设备及节点
- [bzoj1975][Sdoi2010]魔法猪学院 k短路 dijkstra
- POJ 2387 Til the Cows Come Home(最短路dijkstra)
- HDU1690(最短路 两种解法 Dijkstra和Floyd)
- 2017 第十场多校训练 HDU 6181 Two Paths 次短路+Dijkstra
- L2-001. 紧急救援(最短路dijkstra)
- Ubuntu下Swift单节点安装测试小结
- LightOJ 1099 - Not the Best(Dijkstra次短路)
- hdu 2112 最短路 dijkstra优先队列
- poj 3255 Roadblocks Dijkstra求次短路
- CodeForces 20C Dijkstra? (最短路)