最短路算法小结

poj1860,poj3259,poj1062,poj2253,poj1125,poj2240
邻接矩阵(Adjacency Matrix):是表示顶点之间相邻关系的矩阵。
http://www.cnblogs.com/twjcnblog/archive/2011/09/07/2170306.html
http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html
http://blog.csdn.net/zrjdds/article/details/6728332
http://www.cnblogs.com/twjcnblog/archive/2011/09/07/2170306.html
STL http://blog.csdn.net/zy_dreamer/article/details/8939926
SFPA http://blog.sina.com.cn/s/blog_a46817ff01015g9h.html
http://www.cnblogs.com/hxsyl/archive/2012/07/01/2572334.html 115
*****Dijkstra-----
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define INF 0xffffff
#define M 201

int cost[M][M];//路径的权值
int d[M];//s到x最短距离
int used[M];//点是否用过
int n,m;

int s,e;//start end
int a,b,c;


void init(int v)//初始化 cost
{
    for(int i=0;i<v;i++)
        for(int j=0;j<v;j++)
        {
            cost[i][j]=INF;
        }
}

void dj(int s)
{
    int i,j;
    for(i=0;i<=n;i++)
    {
        d[i]=cost[s][i];
    }
    used[s]=1;
    d[s]=0;
/* 选其余点中距离s最短的那个点开始*/
    int k,p;
    for(i=0;i<=n;i++)
    {
        int mi=INF;
        for(j=0;j<n;j++)
        {
            if(!used[j] && mi>d[j])
            {
                mi = d[j];
                k = j;
            }
        }
        used[k] = true;

        for(p=0;p<n;p++)
        {
            if(!used[p]&&d[p]>d[k]+cost[k][p])//d[ax]+d[xb]<d[ab]不断更新dis
            {
                d[p]=d[k]+cost[k][p];
            }
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        init(n);
        for(int i=0;i<m;i++)
        {
            cin>>a>>b>>c;
            if(cost[a][b]>c) {cost[a][b]=c;cost[b][a]=c;}//有权值的正常 没有权值的为INF
        }
        memset(used,0,sizeof(used));
        cin>>s>>e;

        dj(s);
        if(d[e]<INF) cout<<d[e]<<endl;
        else cout<<-1<<endl;
    }
    return 0;
}
**********************************************
#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #define MAX 0x7FFFFFFF
 #define N 1005
 int cls[N],map[N][N],vis[N],city[101],ans[101];
 int m,n,p,q;
 /*V为源点 */
 void Dijkstra(int v)
 {
     int i,j,min,next;
     /* 先用v到邻接点的距离初始化cls */
     for(i=1;i<=m;i++)
        cls[i]=map[v][i];
     memset(vis,0,sizeof(vis));
     vis[v]=1;//起点访问标志置1
     for (i=1;i<m;i++)//无等号
     {
         min=MAX;
         next=v;
         /*找出离集合最近的那个点next,以及该点到集合的距离min*/
         for (j=1;j<=m;j++)
         {
             if(!vis[j]&&cls[j]<min)
             {
                 next=j;
                 min=cls[j];
             }
         }
         vis[next]=1;
         for (j=1;j<=m;j++)
         {
            /*j点没有被访问过,j点和next点之间有通路,next点到j点的距离+集合到nxt的距离<集合到j的距离*/
             if(!vis[j]&&map[next][j]<MAX&&(min+map[next][j])<cls[j])
                 cls[j]=cls[next]+map[next][j];
         }
     }
 }
 int main()
 {
     int T,i,j;int a,b,c;int temp;
     scanf("%d",&T);
     while(T--)
     {
        temp=0x7FFFFFFF;
        for(i=0;i<N;i++)
            for(j=0;j<N;j++)
                map[i][j]=MAX;
        scanf("%d%d%d%d",&n,&m,&p,&q);
        /*  部队驻扎点 */
        for(i=1;i<=n;i++)
            scanf("%d",city+i);
         while(p--)
         {
             scanf("%d%d%d",&a,&b,&c);
             if(map[a][b]>c)
                map[a][b]=map[b][a]=c;
         }
         for(i=1;i<=n;i++)
            {
                memset(cls,0,sizeof(cls));
                Dijkstra(city[i]);
                ans[i]=cls[q];
                //printf("%d     %d       %d\n",cls[q],ans[i],cls[n]);
            }
        for(i=1;i<=n;i++)
            if(temp>ans[i])
                temp=ans[i];
        printf("%d\n",temp);
     }
     return 0;
 }
 /*
 error: invalid types `int[int]' for array subscript
 变量和数组同名
 */

******************************************************************************************************************
#include<iostream>
#include<stdio.h>
using namespace std;
#define inf 9999999
        int map[250][250];
        int n,m;
        int Floyd(int x,int y)
{
int t,i,j;
for(t=0; t<n; t++)
    for(i=0; i<n; i++)
        for(j=0; j<n; j++)
            if(map[i][j]>map[i][t]+map[t][j])
            {
                map[i][j]=map[i][t]+map[t][j];
            }
if(map[x][y]==inf) return -1;
else return map[x][y];
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int a,b,v,d,f,num;
        for(int i=0; i<=n; i++)
            for(int j=0; j<=n; j++)
                map[i][j]=map[j][i]=inf;
        for(int i=0; i<n; i++) //注意同一点的权值为0,刚开始wa在这儿
            map[i][i]=0;
        for(int i=0; i<m; i++)
        {
            cin>>a>>b>>v;
            if(map[a][b]>v)
                map[a][b]=map[b][a]=v;
        }
        cin>>d>>f;
        num=Floyd(d,f);
        cout<<num<<endl;
    }
    return 0;
}
******************************************************************
SPFA 
算法大致流程是用一个队列来进行维护。
初始时将源加入队列。每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法
SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。
SPFA的实现甚至比Dijkstra或者Bellman_Ford还要简单:
设Dist代表S到I点的当前最短距离,Fa代表S到I的当前最短路径中I点之前的一个点的编号。开始时Dist全部为+∞,只有Dist[S]=0,Fa全部为0。
维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点S。用一个布尔数组记录每个点是否处在队列中。
每次迭代,取出队头的点v,依次枚举从v出发的边v->u,设边的长度为len,判断Dist[v]+len是否小于 Dist[u],若小于则改进Dist[u],
将Fa[u]记为v,并且由于S到u的最短距离变小了,有可能u可以改进其它的点,所以若u不在队列中,就将它放入队尾。这样一直迭代下去直到队列变空,
也就是S到所有的最短距离都确定下来,结束算法。若一个点入队次数超过n,则有负权环。
SPFA 在形式上和宽度优先搜索非常类似,不同的是宽度优先搜索中一个点出了队列就不可能重新进入队列,
但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进,
于是再次用来改进其它的点,这样反复迭代下去。设一个点用来作为迭代点对其它点进行改进的平均次数为k,有办法证明对于通常的情况,k在2左右。

<span style="color:#339999;">/*
单源最短路径的算法最常用的是Dijkstra,些算法从时间复杂度来说为O(n^2),
但是面对含有负权植的图来说就无能为力了,此时Dellman-ford算法就有用了,
这咱算法是采用的是动态规化的思想,
但是1994年西南交通大学段凡丁发表了SPFA(Shortest Path Faster Algorithm)听这个名字就懂了,
这种算法在时间上一定很快了。它是对Dellman-ford的优化,
所以建议今后直接学SPFA。很多时候,给定的图存在负权边,
这时类似Dijkstra等算法便没有了用武之地,
而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。

我们用数组d记录每个结点的最短路径估计值,
而且用邻接表来存储图G。我们采取的方法是动态逼近法:
设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,
并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,
如果v点的最短路径估计值有所调整,
且 v点不在当前的队列中,就将v点放入队尾。
这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
  定理3 只要最短路径存在,上述SPFA算法必定能求出最小值。

在Bellman-Ford算法中,要是某个点的最短路径估计值更新了,
那么我们必须对所有边指向的终点再做一次松弛操作;在SPFA算法中,
某个点的最短路径估计值更新,
只有以该点为起点的边指向的终点需要再做一次松弛操作
只要最短路径存在,上述SPFA算法必定能求出最小值。*/
#include<iostream></span>
#include<algorithm>
#include<queue>
#include<stdio.h>
#include<string.h>
#define Maxn 100
#define Maxm 10000
#define Max 10000
using namespace std;

int used[Maxn],outqueue[Maxn],head[Maxn],low[Maxn],n,m;

struct Edge
{
       int to,w,next;
}edge[Maxm];

int SPFA (int start)
{
     queue<int>a;
     used[start] = 1;
     low[start] = 0;
     a.push(start);
     while (!a.empty())
     {
           int top = a.front();
           a.pop();
           outqueue[top]++;
           if (outqueue[top] > n) return false;//用来判断是否有环路
           for (int k = head[top]; k!= -1; k = edge[k].next)//宽搜每一条边
           {
               if (low[edge[k].to] > low[top] + edge[k].w)//对点进行松弛
                  low[edge[k].to] = low[top] + edge[k].w;
               if (!used[edge[k].to])
               {
                   used[edge[k].to] = 1;
                   a.push(edge[k].to);
               }
           }
     }
     return true;
}

int main()
{
    while (scanf ("%d%d", &n ,&m) != EOF)
    {
          memset (used, 0 ,sizeof(used));
          memset (head, -1 ,sizeof(head));
          memset (outqueue, 0 ,sizeof(outqueue));
          memset (low, Max, sizeof(low));
          int k = 0;
          while (m--)
          {
                int a,b,w;
                scanf ("%d%d%d", &a, &b, &w);
                edge[k].to = b;
                edge[k].w = w;
                edge[k].next = head[a];
                head[a] = k++;
          }
          cout<<SPFA(1)<<endl;
          if (SPFA(1))
             printf ("%d\n", low[n]);
          else
             printf ("no\n");
    }
}

************************************************************
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define inf 999999
#define M 2001

int n,m,s;
int dis[M];
int vis[M];
int map[M][M];

void init()
{
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
        {
            map[i][j]=inf;
        }
}
//void dj(int s)
//{
//    int i,j,k = 0;
//    int min;
//    memset(vis,0,sizeof(vis));  //初始时都没被访问过
//    for(i = 1; i <=n; i++)
//    {
//        dis[i] = map[s][i];
//    }
//    vis[s] = 1;  //第一个顶点即原点,被访问
//    dis[s] = 0; //s到s 即为0
//    for(i = 1; i < n; i++)
//    {
//        min = inf;
//        //找到最小的
//        for (j = 1; j <= n; j++)
//        {
//            if (!vis[j] && dis[j] < min)
//            {
//                min = dis[j];
//                k = j;   //最小的顶点
//            }
//        }
//        if (min == inf)
//        {
//            break;
//        }
//        vis[k] = 1; //k顶点被访问
//        //这里就是比较:直接到还是转一下再到
//       //我猜,你个笨蛋肯定会问:不能转多下吗?
//        //答曰:从源点一个一个向外扩展,具有最优子结构。每个dis[i]都保留着从s到i的最短距离。转多下的情况把它分解开,就知道了
//        for(j = 1; j <= n; j++)
//        {
//            if ( !vis[j] && dis[j] > dis[k] + map[k][j])
//            {
//                dis[j] = dis[k] + map[k][j];
//            }
//        }
//    }
//    return ;
//}
void dj(int s)
{
    int i,j,k=0;
    int mi;
    memset(vis,0,sizeof(vis));
    for(i=1;i<=n;i++)
        dis[i]=map[s][i];
    vis[s]=1;
    dis[s]=0;
    for(i=1;i<n;i++)
    {
        mi=inf;
        for(j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]<mi)
            {
                mi=dis[j];
                k=j;
            }
        }
        if(mi==inf)
            break;
        vis[k]=1;
        for(j=1;j<=n;j++)
        {
            if(!vis[j]&&dis[j]>dis[k]+map[k][j])
                {
                    dis[j]=dis[k]+map[k][j];
                }
        }
    }
}
int main()
{
    int p,q,t,w,ww;//起点 终点 所耗费的时间
    int minx;
    while(~scanf("%d%d%d",&n,&m,&s))
    {
        memset(dis,0,sizeof(dis));

        init();//地图初始化

        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&p,&q,&t);
            if(t<map[q][p])
                map[q][p]=t;//因为是把目的地看做源点 所以必须所有的点顺得反过来
        }

        dj(s);
        scanf("%d",&w);
        minx=inf;

        for(int i=1; i<=w; i++)
        {
            scanf("%d",&ww);
            if(minx>dis[ww])
                minx=dis[ww];
        }
        if(minx!=inf)
            printf("%d\n",minx);
        else
            printf("-1\n");
    }
}
//void dj(int s)
//{
//    int i,j,k=0;
//    int mi;
//    memset(vis,0,sizeof(vis));
//    for(i=1; i<=n; i++)
//    {
//        dis[i]=map[s][i];
//    }
//    vis[s]=1;
//    dis[s]=0;//s→s为0
//    for(i=1; i<n; i++)
//    {
//        mi=inf;
//        for(j=1; j<=n; j++)
//        {
//            if(!vis[j]&&dis[j]<mi)
//            {
//                mi=dis[j];
//                k=j;
//            }
//        }
//        if(mi=inf)
//            break;
//        vis[k]=1;//k被访问过
//        for(j=1; j<=n; j++)
//        {
//            if(!vis[j]&&dis[j]>dis[k]+map[k][j])
//            {
//                dis[j]=dis[k]+map[k][j];
//            }
//        }
//
//    }
//    return ;
//}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值