最短路小结 Floyd + Dijkstra + 带花费 + 字符节点

Floyd

这里写图片描述

ps:有向图是带有方向的图,就是有箭头的图,此介绍以有向图为例,无向图不计方向。

初始工作把这张有向图用一个二维数组存储,第一个下表表示起点,第二个表示终点,数组值代表长度。
如下图:

1234
10264
203
3701
45120

自己到自己的距离为 0

从表中看的出,部分路径并不是最短,而且有些路径没有表示出来,所以我们需要更新这个二维数组。

那么如果两点之间不是最短距离,那么需要一个点来中专减少距离
比如:由 4 到 3,直接的话距离为 12,但是经过 节点1 的中转, 变为了 11 ,如果经过 1 和 2 的中转,变为了 10。

假设现在只允许通过 节点1 中转,假设现在从 ij 点,那么长度为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];
1234
10254
29034
36801
457100

这时候可能会问,为什么从 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

这里写图片描述

首先还是需要一个二维数组来记录

123456
10112
209
305
4401314
504
60

这次不在G数组中更新数值,而是创建一个 dis 数组,来记录长度。

123456
dis0112

我们设起点为 1 , 那么dis[1] = 0;(设谁为起点谁为0)

因为是求 1 到其他个点的长度,那么首先就是想找离 1 最近的点,那就是 2 点, 选择了2号顶点, 而且没有中转比它还小,因为 2 号点是里面最小的点了,没有谁能比它还小,而且还需要中转。

那么找到2号顶点之后,引申出两个点34
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]
最后如图:

123456
dis01841317

小总结

先说读取,把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;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值