zoukankan      html  css  js  c++  java
  • 最短路径 Floyd && spfa

    floyd算法是一个很强大的算法,它可以计算任意两点之间的最短路径,其边可以为负值。
    时间复杂度n^3

     1 void floyd()
     2 {
     3     int k,u,v;
     4     for(k=0;k<G.vunm;k++)
     5         for(u=0;u<G.vunm;u++)
     6             for(v=0;v<G.vunm;v++)
     7             {
     8                 if(D[u][v]>D[u][k]+D[k][v])
     9                 {
    10                     D[u][v]=D[u][k]+D[k][v];
    11                 }
    12             }
    13     printf("%d
    ",D[s][t]==MAX?-1:D[s][t]);
    14 }
    Floyd 模板

    补充一下:对于floyd判断负环是否存在只需检查是否存在d[i][i]是负数的顶点i 即可

    传送门: http://poj.org/problem?id=3259

    SPFA算法 :设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

    SPFA 是这样判断负环的: 如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<queue>
     5 #define MAX 999999999
     6 
     7 using namespace std;
     8 
     9 int G[503][503];
    10 int n; //n为点数
    11  
    12 bool SPFA()
    13 {
    14     int u,i;
    15     queue<int>que;
    16     int dis[503];//存最短距离
    17     bool vis[503];//是否访问
    18     int flag[503];//判断是否为负环
    19     
    20     memset(flag,0,sizeof(flag));
    21     memset(vis,false,sizeof(vis)) ;
    22     fill(dis,dis+n+1,MAX);
    23     dis[1]=0;
    24     que.push(1);
    25     while(!que.empty())
    26     {
    27         u=que.front();
    28         que.pop();    vis[u]=false;
    29         for(i=1;i<=n;i++)    //对每个顶点更新相当于BF中通过每个边更新一样 
    30         {
    31             if(dis[i]>dis[u]+G[u][i])
    32             {
    33                 dis[i]=dis[u]+G[u][i];
    34                 if(!vis[i])//可能多次进入。如果之前进入,则不用再进,因为不用进入也可以i的最短路径值,如果在进入,就累赘了。 
    35                 {
    36                     vis[i]=true;    
    37                     flag[i]++;
    38                     if(flag[i]>=n)  //表示存在负环; 
    39                         return true;
    40                     que.push(i);
    41                 }
    42             }
    43         }
    44     }
    45     return false;
    46 }
    47 
    48 int main()
    49 {
    50     int t,k,i,j,u,v,w,m;
    51     
    52     scanf("%d",&t);
    53     while(t--)
    54     {
    55         scanf("%d%d%d",&n,&m,&k);
    56         for(i=1;i<=n;i++)
    57         for(j=1;j<=n;j++)
    58             G[i][j]=i==j?0:MAX;  //i=j表示值0 否则为max 
    59         for(i=0;i<m;i++)
    60         {
    61             scanf("%d%d%d",&u,&v,&w);
    62             G[u][v]=G[v][u]=w;
    63         }
    64         for(i=0;i<k;i++)
    65         {
    66             scanf("%d%d%d",&u,&v,&w);
    67             G[u][v]=-w;  //一定加符号,负权 
    68         } 
    69         printf("%s
    ",SPFA()?"YES":"NO");
    70     }
    71     return 0;
    72 }
    73 
    74  
    SPFA模板

    SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾

    SPFA算法对于稀疏图才能发挥它的大作用,对于稀疏图我们用到的数据结构为  前向星

    下面就是 SPFA+前向星的程序  并应用了SLF  双向队列进行优化

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<queue>
     5 #define MAX 9999999
     6 
     7 using namespace std;
     8 
     9 struct node {
    10     int v,w;//v终点,w权值 
    11     int nest;//下一个 
    12 }; 
    13 node edge[5203];//前向星  每个元素即每条边
    14 int head[503];//头指针式的数组 即邻接链表的指针数组
    15 int cnt;//下标
    16 int n;//点的个数
    17 
    18 void add(int u,int v,int w)//加边  
    19 {
    20     edge[cnt].v=v;
    21     edge[cnt].w=w;
    22     edge[cnt].nest=head[u];//相当于链表头插入法 
    23     head[u]=cnt++;    // 
    24 } 
    25 
    26 bool SPFA()
    27 {
    28     int i,u,v;//u从Q中取出的点  v找到的点
    29     int dis[503];//保存每点最短距离 
    30     int flag[503];//保存某点加入队列的次数
    31     bool vis[503];//标记数组
    32     deque<int> que;//双向队列
    33     
    34     fill(dis,dis+n+1,MAX);
    35     memset(flag,0,sizeof(flag));
    36     memset(vis,false,sizeof(vis));
    37     dis[1]=0;//s为1  即1为起点 
    38     que.push_back(1);//将1加入队列
    39     while(!que.empty()) //队列不为空 
    40     {
    41         u=que.front();//队列中取出
    42         que.pop_front();//删除 
    43         vis[u]=false;//标记为未访问
    44         for(i=head[u];i!=-1;i=edge[i].nest) //对所有与该点相邻的边进行查找 
    45         {
    46             v=edge[i].v;
    47             if(dis[v]>dis[u]+edge[i].w)
    48             {
    49                 dis[v]=dis[u]+edge[i].w;//松弛成功
    50                 if(!vis[v])// 表示未标记
    51                 {
    52                     vis[v]=true;//标记
    53                     flag[v]++;//表示该点进入队列的次数 
    54                     if(flag[v]>=n)//若该点进入队列次数超过n次 说明有负环
    55                         return true;//返回有负环 
    56                     //以下为SLF优化
    57                     if(!que.empty()&&dis[v]<dis[que.front()]) //若为队列为空&&队列队首元素距离大于当前点的距离
    58                         que.push_front(v);//加入到队首
    59                     else
    60                         que.push_back(v); 
    61                  } 
    62              }     
    63         }    
    64     }
    65     return false;//没有负环 
    66 }
    67 
    68 int main()
    69 {
    70     int u,v,w,m,k,t;
    71     
    72     scanf("%d",&t);
    73     while(t--)
    74     {
    75         memset(head,-1,sizeof(head));
    76         cnt=0;
    77         scanf("%d%d%d",&n,&m,&k);
    78         while(m--)
    79         {
    80             scanf("%d%d%d",&u,&v,&w);
    81             add(u,v,w);    add(v,u,w); //双向无向图 
    82         }
    83         while(k--)
    84         {
    85             scanf("%d%d%d",&u,&v,&w);
    86             add(u,v,-w);
    87         }
    88         printf("%s
    ",SPFA()?"YES":"NO");
    89     }
    90     return 0; 
    91 }
    92  
    SPFA优化
  • 相关阅读:
    MongoDB安装&启动
    MongoDB集群搭建
    树与二叉树
    git入门
    MongoDB Java Driver
    Spring整合Junit4
    SQL字符串的数字部分递增
    [求职经历反面教材]4周面试20家,面霸磨成面瘫,仅供初级程序员参考!
    简陋的信息采集方式
    由一个博问学到的SQL查询方法 (一道多对多关系查询的面试题)
  • 原文地址:https://www.cnblogs.com/WDKER/p/5183771.html
Copyright © 2011-2022 走看看