最短路径题集1

首先来个板子,单源最短路径,Dijkstra算法。

杭电2544

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2544
Dijkstra需要的数据结构有4个数组,在代码中说明了每个数据结构的用途。
首先把第起点放入S集合中,然后找出不在S集合中的离起点最近的点,将其加入到S集合中。再对dis数组进行更新,看是否经过该点能够使路径长度变短。直到将所有点都加入到S集合中。
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxNum = 0x3f3f3f3f
int f[101][101];  //矩阵,i到j的cost
int dis[101];  //存储从1到i的最短路径长度,即时更新
int s[101];  //S集合
int path[101]; //存储从1到n的最短路径所经过的点,便于输出路径
int main()
{
    int m, n;
    int a, b, c;
    while (true){
        scanf("%d%d", &n, &m);
        if (m == 0 && n == 0) break;
        memset(f, 0x3f, sizeof(f));  //初始化矩阵为最大值
        memset(s, 0, sizeof(s));
        for (int i = 0; i < m; i++){
            scanf("%d%d%d", &a, &b, &c);
            f[a][b] = c;
            f[b][a] = c;
        }
        for (int i = 1; i <= n; i++){
            dis[i] = f[1][i];
        }
        dis[1] = 0; s[1] = 1;

        for (int i = 1; i <= n; i++){
            int minnum = 0x3f3f3f3f;
            int u = 1;//找到不在S集合中,离S集合最近的点
            for (int j = 1; j <= n; j++){
                if (!s[j] && minnum > dis[j]){
                    u = j;
                    minnum = dis[j];
                }
            }
            s[u] = 1; //把最近的那个点,加进集合S
            for (int j = 1; j <= n; j++){  //更新
                if (!s[j] && dis[u] + f[u][j] < dis[j]){
                    dis[j] = dis[u] + f[u][j];
                    path[j] = u;
                }
            }
        }
        printf("%d\n", dis[n]);

    }
    return 0;
}

flod算法,多源最短路径算法,核心只有5行代码,而且只需要一个矩阵数组即可。

POJ1125

原题链接: http://poj.org/problem?id=1125
这题意太难理解了,看了好几遍才看明白。

大意是:有n个股票经纪人,输入信息中有各经纪人之间的消息传递时间。要从某一个经纪人传出消息去,使所有人都能接收到消息。题目要求输出从哪个经纪人作为起点传出消息去,能使所有人都能在最短时间内接收到消息。

用flod计算任意两点之间的最短路径,然后对第i行,找出最大时间来t,这样可以确保第i个人传递消息时,经过t分钟后可以使所有人收到消息。然后对这i个时间t求最小值,同时记录下对应的i。就找到了起点和最短时间。

下面是AC代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include "stdio.h"
using namespace std;
int a[101][101];
int main(){
    int n;
    while (true){
        scanf("%d", &n);
        if (n == 0) break;
        memset(a, 0x3f, sizeof(a));
        for (int i = 1; i <= n; i++){
            int m, x, y;
            scanf("%d", &m);
            for (int j = 1; j <= m; j++){
                scanf("%d%d", &x, &y);
                a[i][x] = y;
            }
        }//输入
        for (int k = 1; k <= n; k++){
            for (int i = 1; i <= n; i++){
                for (int j = 1; j <= n; j++){
                    if (a[i][j] > a[i][k] + a[k][j])
                        a[i][j] = a[i][k] + a[k][j];
                }
            }
        }//flod算法结束
        //下面寻找最小的那个时间,及对应的人
        int ans = 0x3f3f3f3f, person, ans2;
        //ans2表示每一行中最大的,ans表示所有行中最小的,即ans2中的最小值。
        for (int i = 1; i <= n; i++){
            ans2 = 0;
            for (int j = 1; j <= n; j++){
                if (i == j) continue;
                ans2 = max(a[i][j], ans2);
            }
            if (ans > ans2){
                ans = ans2;
                person = i;
            }
        }
        printf("%d %d\n", person, ans);
    }
    return 0;
}

除了上面两种最短路径,还有bellman-ford算法。这个是为了解决Dijkstra算法只能计算非负权值的情况。它在Dijkstra算法的基础上,增加了判断是否存在负环路,如果存在,则肯定不会有最短路径,不存在即可输出最短路径。
如果不存在负环路,那么经过n-1遍更新后,进行第n遍操作时最短路径的数组dis[]肯定不会发生更改,但是如果发生了变化,说明存在负环。核心代码如下:

bool Bellman_Ford()  
{  
    for(int i = 1; i <= nodenum; ++i) //初始化  
        dis[i] = (i == original ? 0 : MAX);  
    for(int i = 1; i <= nodenum - 1; ++i)  //从这里可以看出复杂度是n*m,采用的是结构体数组存储。
        for(int j = 1; j <= edgenum; ++j)  
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //进行松弛更新操作 
            {  
                dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;  
                pre[edge[j].v] = edge[j].u;  //该数组记录的是最短路径的前一个节点编号
            }  
            bool flag = 1; //判断是否含有负权回路  
            for(int i = 1; i <= edgenum; ++i)  
                if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)  
                {  
                    flag = 0;  
                    break;  
                }  
                return flag;  
}  
发布了56 篇原创文章 · 获赞 49 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/IBelieve2016/article/details/73693027