最短路总结

<p><span style="font-size: 18px;">最短路主要有四部分。</span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span>一</span><span style="font-family: Arial, Helvetica, sans-serif;">迪杰斯特拉思想</span></p>
//Dijkstra算法用于计算一个节点到其它所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点。
//基本思想是:设置顶点集合S不断地做贪心选择来扩充这个集合
//初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。


#include <stdio.h>
#define maxnum 100
#define maxint 999999

int dist[maxnum]; //表示当前点到源点的最短路径长度
int prev[maxnum];   //记录当前点的前一个结点
int c[maxnum][maxnum];  //记录图的两点间路径长度
int n,line;            //图的结点数和路径数


void Dij(int n,int v,int *dist, int *prev, int c[maxnum][maxnum]) //v代表源点吗?
{
    int s[maxnum] = {0};//判断是否已将该点存入到s数组中
    int i;
    for(i = 1;i<=n;i++)
    {
        dist[i] = c[v][i];//将从源点到i点的初始距离存入数组dist中
        s[i] = 0;//初始化为全部没有用过该点
        if(dist[i] == maxint)//如果源点到该点的距离为无穷大,则该点前面没有连接点
            prev[i] = 0;
        else
            prev[i] = v;
    }
    dist[v] = 0;    //源点到源点的距离初始化为0
    s[v] = 1;//把源点存入数组s
    
    //依次将未放入集合s中的节点,取dist【】最小值的节点放进去
    //一旦s包含了所有v中顶点,dist就记录了从源点到所有其他顶点之间的最短路径
    //注意是从第二个节点开始,第一个结点表示源点
   for(i = 2;i<=n;i++)
   {
       int tmp = maxint;
       int u = v;
       for(int j = 1;j<=n;j++)  //找出当前未使用的点j的dist【j】最小值
       {
           if((!s[j]) && dist[j] < tmp)
           {
               u = j;               //u储存当前邻接点中距离最小的点的号码
               tmp = dist[j];
           }
       }
       s[u] = 1;        //一次for循环结束后,便找出了所有未使用的点中距离最小的点,将此点存入集合s中
       
       //更新dist值
       
       for(int j = 1;j<=n;j++)
       {
           if((!s[j]) && c[u][j]<maxint) //确定该点没有加入s集合,并且存在这段距离,即经过该点
           {
               int newdist = dist[u] + c[u][j];//判断经过该点到j的距离和不经过该点到j的距离的大小
               if(newdist < dist[j])
               {
                   dist[j] = newdist;
                   prev[j] = u;
               }
           }
       }
   }
}

//查找从源点v到终点u的路径,并输出

void searc(int *prev,int v,int u)
{
    int que[maxnum];        //该数组保存路径
    int tot = 1;
    que[tot] = u;       //从终点开始找,一直找到源点v并记录路径
    tot++;
    int tmp = prev[u];
    while(tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = prev[tmp];
    }
    que[tot] = v;
    for(int i = tot;i>=1;i--)//输出
    {
        if(i!=1)
            printf("%d->",que[i]);
        else
            printf("%d\n",que[i]);
    }
    //注意,若题目要求输出距离,则直接输出dist【u】即可
}

int main()
{
    scanf("%d%d",&n,&line);
    int p,q,len;
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
            c[i][j] = maxint;
    }
    for(int i = 1;i<=line;i++)
    {
        dist[i] = maxint;
    }
    for(int i = 1;i<=line;i++)
    {
        printf("%d%d%d",&p,&q,&len);
        if(len<c[p][q])
        {
            c[p][q] = len;
            c[q][p] = len;
        }
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
        {
            printf("%8d",c[i][j]);
        }
        printf("\n");
    }
    Dij(n,1,dist,prev,c);
    printf("从源点到终点的最短路径长度为%d\n",dist[n]);
    searc(prev,1,n);
    return 0;
}
//下面运用弗洛伊德(Floyd)来解决有关问题
//在该思想中,数组a【】【】初始化为个顶点之间的距离,最后储存各定点之间的最短距离。
//path【】【】数组保存最短路径,与当前迭代的次数有关,初始化都为-1,即没有中间顶点。
//在求a【i】【j】的过程中,path[i][j]存放从顶点i到顶点j的中间顶点的编号不大于k的最短路径上前一个结点的编号
//数组a[][]其实是有向图的邻接矩阵,此类矩阵可以表示无向


//这个程序是
#include <stdio.h>
#include <string.h>


struct MGraph
{
    char vertex[1000];      // 储存各个顶点
    int edges[1000][1000];      //邻接矩阵
    int n,e; // n代表顶点个数,e代表边数
};


void ppath(int path[][1000],int i,int j)   调用函数,输出路径
{
    int k;
    k = path[i][j];
    if(k == -1)
        return;
    ppath(path,i,k);
    printf("%d,",k);
    ppath(path,k,j);
}


void CreateMGraph(MGraph &G)
{
    int i,j,k,p;
    scanf("%d%d",&G.n,&G.e);    //输入顶点数和边数
    for(i = 0;i<G.n;i++)        //输入各个顶点
    {
        scanf("%d",&G.vertex[i]);
    }
    for(i = 0;i<G.n;i++) // 初始化邻接矩阵。若是对角线,即是自己到自己的距离,初始化为0
    {
        for(j = 0;j<G.n;j++)
        {
            G.edges[i][j] = 100000;
            if(i == j)
                G.edges[i][j] = 0;
        }
    }
    for(i = 0;i<G.e;i++) // 输入边的前驱后驱以及权值
    {
        scanf("%d%d%d",&j,&k,&p);
        G.edges[j][k] = p;
    }
}


void Dispath(int A[][1000],int path[][1000],int n)
{
    int i,j;
    for(i = 0;i<n;i++)
    {
        for(j = 0;j<n;j++)
        {
            if(A[i][j] == 100000)
            {
                if(i!=j)
                    printf("从%d到%d没有路径\n",i,j);
            }
            else
            {
                printf("从%d到%d=>路径长度:%d路径:",i,j,A[i][j]);
                printf("%d",i);
                ppath(path,i,j);
                printf("%d\n",j);
            }
        }
    }
}


void Floyd(MGraph &G)
{
    int A[1000][1000],path[1000][1000];
    int i,j,k;
    //初始化a【】【】为最开始的距离,path【】【】代表最短路径,与当前迭代的次数有关
    for(i = 0;i<G.n;i++)
    {
        for(j = 0;j<G.n;j++)
        {
            A[i][j] = G.edges[i][j];
            path[i][j] = -1;
        }
    }
    for(k = 0;k<G.n;k++)// 运用弗洛伊德进行最短路搜索,k作为中间点
    {
        for(i = 0;i<G.n;i++)
        {
            for(j = 0;j<G.n;j++)
            {
                if(A[i][j] > A[i][k] + A[k][j])
                {
                    A[i][j] = A[i][k] + A[k][j];
                    path[i][j] = k;
                }
            }
        }
    }
    Dispath(A,path,G.n);
}




int main()
{
    MGraph G;
    CreateMGraph(G);
    Floyd(G);
    return 0;
}
</pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">
</pre><pre code_snippet_id="204097" snippet_file_name="blog_20140225_5_5274998" name="code" class="html">

贝尔曼—福特思想

//迪杰斯特拉算法是处理单源最短路径的算法,但它局限于边的权值非负的情况
//贝尔曼——福特就可以解决此类问题
//思想:给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,

//数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为, Distant[s]为0;
 
//以下操作循环执行至多n-1次,n为顶点数:
//对于每一条边e(u, v),如果Distant[u] + w(u, v) < Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
//若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
//为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的边,则图中存在负环路,
//即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。


#include <stdio.h>
#include <stdlib.h>
#define maxnum 2000
#define maxint 999999

typedef struct
{
    int v,u;        //终点和起点
    int weight;     //两点之间的权值
} Edge;
Edge edge[maxnum];      //保存边的值
int dist[maxnum];       //保存结点到原点的最短距离

int nodenum,edgenum,source;     //结点数,边数,源点

void init()
{
    scanf("%d%d%d",&nodenum,&edgenum,&source);  //输入结点数,边数,源点
    int i;
    for(i = 1;i <= nodenum;i++)
        dist[i] = maxint;       //把结点到原点的距离都初始化为最大
        dist[source] = 0;       //节点到节点的距离初始化为0
        for(i = 1;i<=edgenum;i++)
        {
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].weight);  //点u到点v的距离
            if(edge[i].u == source)
                dist[edge[i].v] = edge[i].weight;       //如果起点是源点,则把点v到点u(源点)的距离更改为权值weight
        }
}
int BF()        //该函数用于计算最小距离和判断是否有负权值
{
    int i,j,flag = 1;
    for(i = 1;i<=nodenum-1;i++) //外层循环的次数是结点数减一
    {
        for(j = 1;j <= edgenum;j++)//内层循环是边的数目,也就是每次都遍历整个边表
        {
            if(dist[edge[j].v] > dist[edge[j].u] + edge[j].weight)
                dist[edge[j].v] = dist[edge[j].u] + edge[j].weight;
                //如果从源点到第v个点的距离大于从源点到第u个点的距离加上从u点到v点的距离,就改变从源点到第v个点的距离
        }
    }
    for(i = 1;i<=edgenum;i++)      //再一次进行最短路搜寻,在上面的循环中已经求出了最短路线,如果还能求出最短路线,则会出现负环路,每进去一次,都会减小
    {
        if(dist[edge[i].v > dist[edge[i].u + edge[i].weight)
        {
            flag=0;
            break;
        }
    }
    return flag;
}
int main()
{
    init();
    if(BF())
    {
        for(int i = 1;i<=nodenum;i++)
            printf("%d\n",dist[i]);
    }
    else
        printf("-1\n");
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值