zoukankan      html  css  js  c++  java
  • 图论基础之Floyd(弗洛伊德算法&&Spfa算法&&邻接表):畅通工程续 HDU1874&&最短路 HDU2544

    畅通工程续 HDU1874

    因为数据给的较小,可以用Floyd算法。

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 using namespace std;
     5 int main()
     6 {
     7     int MAX=10000;
     8     int a[200][200];
     9     int n,m,i,j,k,x,y,z,s,t;
    10     while(scanf("%d %d",&n,&m)!=EOF)
    11     {
    12         for(i=0;i<n;i++)
    13         for(j=0;j<n;j++)
    14             a[i][j]=MAX;
    15     for(i=0;i<n;i++)
    16         a[i][i]=0;
    17         for(i=0;i<m;i++)
    18         {
    19             scanf("%d%d%d",&x,&y,&z);
    20             if(a[x][y]>z)
    21             {
    22                 a[x][y]=z;a[y][x]=z;
    23             }
    24         }
    25         for(k=0;k<n;k++)
    26             for(i=0;i<n;i++)
    27             for(j=i+1;j<n;j++)
    28         {
    29             if(a[i][j]>a[i][k]+a[k][j])
    30             {a[i][j]=a[i][k]+a[k][j];a[j][i]=a[i][j];}
    31         }//核心:三重递归全面求最小路径。
    32         scanf("%d%d",&s,&t);
    33         if(a[s][t]==MAX)
    34             printf("-1
    ");
    35         else
    36             printf("%d
    ",a[s][t]);
    37     }
    38     return 0;
    39 }

     刚刚学习又一种方法:Spfa法:先上代码:(貌似这种也只能查已知起点的)

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 #include<queue>
     5 #include<string.h>
     6 using namespace std;
     7 int a[205][205],c[205],vis[205],max1=10005;
     8 queue<int> Q;
     9 int main()
    10 {
    11     int n,m,i,j,k,a1,a2,a3,s,t;
    12     while(scanf("%d%d",&n,&m)!=EOF)
    13     {
    14         for(i=0;i<n;i++)
    15             for(j=0;j<n;j++)
    16             a[i][j]=max1;
    17         for(i=0;i<n;i++)
    18             a[i][i]=0;
    19         while(m--)
    20         {
    21             scanf("%d%d%d",&a1,&a2,&a3);
    22             if(a[a1][a2]>a3)
    23             {
    24                 a[a1][a2]=a3;
    25                 a[a2][a1]=a3;
    26             }
    27         }
    28         scanf("%d%d",&s,&t);
    29         memset(vis,0,sizeof(vis));//这个数组是用来判断是否入队的!!(然而没有这个程序也对,但是会增加时间)
    30         for(i=0;i<n;i++)
    31             c[i]=max1;
    32         c[s]=0;
    33         Q.push(s);
    34         while(!Q.empty())//因为这里不小心加了个“;”这个伤心半小时!!!!
    35         {
    36             k=Q.front();
    37             Q.pop();
    38             vis[k]=0;
    39             for(i=0;i<n;i++)
    40                 if(c[i]>c[k]+a[k][i])
    41             {
    42                 c[i]=c[k]+a[k][i];
    43                 if(vis[i]==0)
    44                 {
    45                     Q.push(i);
    46                     vis[i]=1;
    47                 }
    48             }
    49         }
    50         if(c[t]!=max1)
    51             printf("%d
    ",c[t]);
    52         else
    53            printf("-1
    ");
    54     }
    55     return 0;
    56 }

     Spfa算法:

    我们用数组d记录每个结点最短路径估计值,而且用邻接表来存储图G。我们采取的方法是松弛(三角形的一条边>其他两条边之和):设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。(此处可以用queue()也可以模拟一个队列)(效率是最高的吧)

    首先最短路存在,上述的spfa算法必能求出最小值。(不能存在负环,就是负权回路)

    证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证毕)

    判断有无负环:

    如果某个点进入队列次数超过N次则存在负环 (存在负环则无最短路径,如果有负环则会无限松弛,而一个带n个点的图至多松弛n-1次

    Floyd算法适用于APSP(All Pairs Shortest Paths),是一种动态规划算法,稠密图效果最佳,边权可正可负。此算法简单有效,由于三重循环结构紧凑,对于稠密图,效率要高于执行|V|次Dijkstra算法
    优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单
    缺点:时间复杂度比较高,不适合计算大量数据。
    Spfa算法+邻接表
     1 #include<iostream>
     2 #include<algorithm>
     3 #include<stdio.h>
     4 #include<queue>
     5 #include<string.h>
     6 using namespace std;
     7 queue<int> Q;
     8 int vis[10005],b[10005],t,pre[10005];
     9 struct line
    10 {
    11     int left;
    12     int right;
    13     int num;
    14     int Next;
    15 }a[10005];
    16 void add(int left,int right,int num)
    17 {
    18     a[t].left=left;
    19     a[t].right=right;
    20     a[t].num=num;
    21     a[t].Next=pre[left];
    22     pre[left]=t++;
    23 }
    24 int main()
    25 {
    26     int n,m,left,right,num,a1,b1,i,max1,p,k;
    27     max1=10000005;
    28     while(scanf("%d%d",&n,&m)!=EOF)
    29     {
    30         memset(pre,-1,sizeof(pre));
    31         t=1;
    32         while(m--)
    33         {
    34             scanf("%d%d%d",&left,&right,&num);
    35             if(left!=right)
    36             {
    37                 add(left,right,num);
    38                 add(right,left,num);
    39             }
    40         }
    41         scanf("%d%d",&a1,&b1);
    42         memset(vis,0,sizeof(vis));
    43         for(i=0;i<n;i++)
    44             b[i]=max1;
    45         b[a1]=0;
    46         Q.push(a1);
    47         while(!Q.empty())
    48         {
    49             p=Q.front();
    50             Q.pop();
    51             vis[p]=0;
    52             for(i=pre[p];i+1;i=a[i].Next)
    53             {
    54                 k=a[i].right;
    55                 if(b[k]>b[p]+a[i].num)//比较的是一个表的左端点,本身,右端点的值
    56                 {
    57                     b[k]=b[p]+a[i].num;
    58                     if(!vis[k])
    59                     {
    60                         Q.push(k);
    61                         vis[k]=1;
    62                     }
    63                 }
    64             }
    65         }
    66         if(b[b1]!=max1)
    67             printf("%d
    ",b[b1]);
    68         else
    69             printf("-1
    ");
    70     }
    71     return 0;
    72 }

    最短路  HDU2544

    和上面的题目类似:先用Floyd算法写着。

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 using namespace std;
     5 int MAX=1000005;
     6 int main()
     7 {
     8     int a[105][105];
     9     int n,m,i,j,k,x,y,z;
    10     while(scanf("%d %d",&n,&m)!=EOF)
    11     {
    12         if(m==0&&n==0)
    13             break;
    14         for(i=1;i<=n;i++)
    15             for(j=1;j<=n;j++)
    16                 a[i][j]=MAX;
    17         for(i=1;i<=n;i++)
    18             a[i][i]=0;
    19         for(i=1;i<=m;i++)
    20         {
    21             scanf("%d%d%d",&x,&y,&z);
    22                 if(a[x][y]>z)
    23                 {a[x][y]=z;a[y][x]=a[x][y];}
    24         }
    25         for(k=1;k<=n;k++)
    26         for(i=1;i<=n;i++)
    27         for(j=i+1;j<=n;j++)
    28         {
    29             if(a[i][j]>a[i][k]+a[k][j])
    30             {a[i][j]=a[i][k]+a[k][j];a[j][i]=a[i][j];}
    31         }
    32         printf("%d
    ",a[1][n]);
    33     }
    34     return 0;
    35 }

     再是用Dijkstra算法做的:(都是开个二维数组,赋值都是一样的!)

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 #include<string.h>
     5 using namespace std;
     6 int a[1005][1005],w[1005];
     7 int main()
     8 {
     9     int n,m,i,j,p,min1,a1,a2,a3;
    10     while(scanf("%d%d",&n,&m)!=EOF)
    11     {
    12         if(n==0&&m==0)
    13             break;
    14         for(i=1;i<=n;i++)
    15             for(j=1;j<=n;j++)
    16                 a[i][j]=100000;
    17         for(i=1;i<=n;i++)
    18             a[i][i]=0;
    19         while(m--)
    20         {
    21             scanf("%d%d%d",&a1,&a2,&a3);
    22             if(a[a1][a2]>a3)
    23             {
    24                 a[a1][a2]=a3;
    25                 a[a2][a1]=a3;
    26             }
    27         }
    28         memset(w,0,sizeof(w));
    29         for(i=1;i<=n;i++)
    30         {
    31             min1=100000;
    32             for(j=1;j<=n;j++)
    33                 if(!w[j]&&min1>a[1][j])
    34                 {
    35                     p=j;
    36                     min1=a[1][j];
    37                 }
    38             w[p]=1;//找个中间点来比较!!
    39             for(j=1;j<=n;j++)
    40                 if(a[1][j]>a[1][p]+a[p][j]&&!w[j])
    41                     {
    42                         a[1][j]=a[1][p]+a[p][j];
    43                         a[j][1]=a[1][j];
    44                     }
    45         }
    46         printf("%d
    ",a[1][n]);
    47     }
    48     return 0;
    49 }

     之后用Spfa法做的:(都是开个二维数组,赋值都是一样的)

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 #include<string.h>
     5 #include<queue>
     6 using namespace std;
     7 int a[1005][1005],c[1005],vis[1005];
     8 int max1=100000005;
     9 int main()
    10 {
    11     int n,m,i,j,a1,a2,a3,k;
    12     queue<int> Q;
    13     while(scanf("%d%d",&n,&m)!=EOF)
    14     {
    15         if(n==0&&m==0)
    16             break;
    17         for(i=1;i<=n;i++)
    18             for(j=1;j<=n;j++)
    19             a[i][j]=max1;
    20         for(i=1;i<=n;i++)
    21             a[i][i]=0;
    22         while(m--)
    23         {
    24             scanf("%d%d%d",&a1,&a2,&a3);
    25             if(a[a1][a2]>a3)
    26             {
    27                 a[a1][a2]=a3;
    28                 a[a2][a1]=a3;
    29             }
    30         }
    31         memset(vis,0,sizeof(vis));///***************************(判断是否入队)
    32         for(i=1;i<=n;i++)
    33             c[i]=max1;//用来记录值的。。。
    34         c[1]=0;//起点为0,貌似这个算法也要知道起点
    35         Q.push(1);
    36         while(!Q.empty())
    37         {
    38             k=Q.front();
    39             Q.pop();
    40             vis[k]=0;
    41             for(i=1;i<=n;i++)
    42                 if(c[i]>c[k]+a[k][i])
    43             {
    44                 c[i]=c[k]+a[k][i];
    45                 if(vis[i]==0)//因为c【i】值变小了,他就会对其他的值进行影响,所以把他放到队列里去(如果在里面就不用了)
    46                 {
    47                     Q.push(i);
    48                     vis[i]=1;
    49                 }
    50             }
    51         }
    52         printf("%d
    ",c[n]);///********************************变的是这个地方
    53     }
    54     return 0;
    55 }

     通过不懈的努力终于看懂邻接表了!!

    邻接表是图的一种链式存储结构。对图的每个顶点建立一个单链表(n个顶点建立n个单链表),第i个单链表中的结点包含顶点Vi的所有邻接顶点。又称链接表。
    补充:
    1.在有向图的邻接表中不易找到指向该顶点的弧。
    2.在有向图的邻接表中,对每个表头结点,链接的是以该顶点为起始点射入的尾结点,该尾结点为邻接点。
    反正很难理解,但是看懂代码是好说了。。。
    最短路的Spfa法+邻接表:(首先声明下面所说的邻接点是指左端点相同所对应的边)
     1 #include<iostream>
     2 #include<algorithm>
     3 #include<stdio.h>
     4 #include<string.h>
     5 #include<queue>
     6 using namespace std;
     7 int vis[10005],b[10005],pre[10005],t;//pre[]表示是邻接表的指针
     8 struct line
     9 {
    10     int left;//一条边的左端点
    11     int right;//一条边的右端点
    12     int num;//一条边的长度
    13     int Next;//指向下一条边的指针的储存数组(存储邻接点的)
    14 }a[10005];
    15 void add(int left,int right,int num)
    16 {
    17     a[t].left=left;
    18     a[t].right=right;
    19     a[t].num=num;
    20     a[t].Next=pre[left];//记录的是边左端点的指针(指针不为-1说明有临界点(就是left值一样的))
    21     pre[left]=t++;
    22 }
    23 int main()
    24 {
    25     int n,m,left,right,num,i,p,k,max1=100000005;
    26     queue<int> Q;
    27     while(~scanf("%d%d",&n,&m)&&n&&m)
    28     {
    29         memset(pre,-1,sizeof(pre));
    30         t=1;
    31         while(m--)
    32         {
    33             scanf("%d%d%d",&left,&right,&num);
    34             if(left!=right)
    35             {
    36                 add(left,right,num);//这里是无向图,要进行两次入表
    37                 add(right,left,num);
    38             }
    39         }
    40         memset(vis,0,sizeof(vis));
    41         for(i=1;i<=n;i++)
    42             b[i]=max1;
    43         b[1]=0;
    44         Q.push(1);
    45         while(!Q.empty())
    46         {
    47             p=Q.front();
    48             Q.pop();
    49             vis[p]=0;
    50             for(i=pre[p];i+1;i=a[i].Next)//先更新再判断是否有邻接点(i+1等价于i!=-1)
    51                 {
    52                     k=a[i].right;//更新右端点的值
    53                     if(b[k]>a[i].num+b[p])//此处的b[p]是表示左端点的值
    54                     {
    55                         b[k]=a[i].num+b[p];//更新
    56                         if(!vis[k])
    57                         {
    58                             Q.push(k);
    59                             vis[k]=1;
    60                         }
    61                     }
    62                 }
    63         }
    64         printf("%d
    ",b[n]);
    65     }
    66     return 0;
    67 }

    代码的50行时,我们发现从起点开始(题目中的1)先是看1有没有邻接点。。。

    如果没有,看1对应的右端点假设是2有没有邻接点。。。

    直到没有。。。

    例子:

    3 3

    1 2 5

    1 3 10

    2 3 4                                               O1                                                                                        

    那么点1的邻接点有一个除自身以外)  O1-O2-O-O-O

     点2的邻接点没有。。。但是是先比较再去看有没有邻接点的

  • 相关阅读:
    JS——变量和函数的预解析、匿名函数、函数传参、return
    JS——函数
    JS——操作元素属性
    JS——变量声明、变量类型、命名规范
    JS——三种嵌入页面的方式
    CSS——弹性盒模型
    CSS——相对定位、绝对定位、固定定位
    CSS3——PC以及移动端页面适配方法(响应布局)
    [20180528]校内模拟赛
    Leetcode Majority Element系列 摩尔投票法
  • 原文地址:https://www.cnblogs.com/tt123/p/3224524.html
Copyright © 2011-2022 走看看