浅谈最短路

  最短路问题(short-path problem)是网络理论解决的典型问题之一,可用来解决管路铺设、线路安装、厂区布局和设备更新等实际问题。基本内容是:若网络中的每条边都有一个数值(长度、成本、时间等),则找出两节点(通常是源节点和阱节点)之间总权和最小的路径就是最短路问题。(摘自百度百科)

  常见的最短路算法有floyd,dijkstra和spfa。

1.floyd

  floyd可以说是最暴力的最短路求法了,但其实floyd是用的动态规划的思想,只不过floyd可以求出任意2点之间的最短路,所以时间复杂度O(n^3)。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int a[110][110];
 6 int x,y,v;
 7 int main()
 8 {
 9     int n,m;//n个点,m条边
10     memset(a,127/3,sizeof(a));
11     for(int i=1;i<=m;++i)
12     {
13         scanf("%d%d%d",&x,&y,&v);
14         a[x][y]=a[y][x]=v;//双向边 
15     }
16     for(int k=1;k<=n;++k)
17         for(int i=1;i<=n;++i) 
18             for(int j=1;j<=n;++j) 
19                 a[i][j]=min(a[i][k]+a[k][j],a[i][j]);//松弛操作
20     printf("%d",a[1][n]);
21     return 0; 
22 }
View Code

  例题:https://www.luogu.org/problemnew/show/P2888

  多组询问,n<=300,裸的floyd,但是这题稍微变一下形,因为不是求路径长度,而是求路上最高的跨栏的高度,只需要把floyd的加和变成取max就好了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,m,t;
 6 int a[310][310];
 7 int tmp1,tmp2,tmp3;
 8 int read()
 9 {
10     int x=0,f=1;
11     char ch=getchar();
12     while(!isdigit(ch))
13     {
14         if(ch=='-')f=0;ch=getchar();
15     }
16     while(isdigit(ch))
17     {
18         x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
19     }
20     return f?x:-x;
21 
22 }
23 int main()
24 {
25     memset(a,127/3,sizeof(a));
26     n=read(),m=read(),t=read();
27     for(int i=1;i<=m;++i)
28     {
29         tmp1=read(),tmp2=read(),tmp3=read();
30         a[tmp1][tmp2]=tmp3;
31     }
32     for(int k=1;k<=n;++k)
33         for(int i=1;i<=n;++i)
34             for(int j=1;j<=n;++j)
35                 if(max(a[i][k],a[k][j])<a[i][j])a[i][j]=max(a[i][k],a[k][j]);
36     for(int i=1;i<=t;++i)
37     {
38         tmp1=read(),tmp2=read();
39         if(a[tmp1][tmp2]<214748364)cout<<a[tmp1][tmp2]<<endl;
40         else cout<<-1<<endl;
41     }
42     return 0;
43 }
View Code

  floyd还可以用来求图的连通性,判断2个点是否联通

1 for(int k=1;k<=n;++k)
2     for(int i=1;i<=n;++i)
3         for(int j=1;j<=n;++j)
4             f[i][j]|=(f[i][k]&f[j][k])//如果i j本身联通,就是联通了,如果i k联通,k j联通,那么i j也联通 
View Code

2.dijkstra

  dijkstra解决的是单源最短路问题,时间复杂度O(n^2),dijkstra是贪心的思想,不能解决存在负边权的图。

  dijkstra算法流程:从起点开始,更新每一个可以被更新的点,然后找出来一个距起点最近的点,用它更新所有可以被更新的点,依次循环n次。在这里可以被更新的点指的是所有用当前点松弛可以得到与起点更近的距离的点。

  算法证明(窝自己瞎yy的,不喜勿喷):

  最开始的时候找到的都是和起点联通的点,那么这里面的距离起点最近的点一定不可能在被其他点更新了,也就是它和起点的最短路就是它和起点连接的这条边了。有一个显而易见的性质就是,一个点的距离起点的最短路上的点到起点都是最短路,因为不是这样的话就会存在长度更小的路径。所以此时用第二个点更新出来的距离起点最小的点的最短路径长度也出来了,在用它去更新别人,依次更新n次,终点的最短路也就找到了。

  我是很少用dijkstra的,因为基本上可以用spfa过。没找到例题,也不知道代码对不对就先不放了。

3.spfa

  spfa应该是大部分oier最熟悉的最短路算法了,spfa之所以叫spfa是因为发明算法的人称之为 special fast algorithm,也就是非常快的意思啦,一般说spfa的复杂度是O(km)的,切k的一般为2,但实际证明并没有那么快,spfa会被稠密图卡。

  例题:https://www.luogu.org/problemnew/show/P3371#sub

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 using namespace std;
 5 int n,m,s;
 6 vector<int>tu[10010];//cun dian
 7 vector<int>v[10010];//cun bian quan
 8 int from,to,val;
 9 int q[10010*4];
10 int head=1,tail=1;
11 bool vis[10010];
12 int dis[10010];
13 void spfa(){
14     q[head]=s;
15     vis[s]=1;
16     while(head<=tail){
17         int k=q[head];
18         vis[k]=0;
19         head++;
20         for(int i=0;i<tu[k].size();++i)
21           {
22               if(dis[k]+v[k][i]<dis[tu[k][i]])
23             {
24               dis[tu[k][i]]=dis[k]+v[k][i];
25                 if(!vis[tu[k][i]])vis[tu[k][i]]=1,q[++tail]=tu[k][i];
26             }    
27          }
28       }
29 }
30 int main(){
31     cin>>n>>m>>s;
32     for(int i=1;i<=n;++i)dis[i]=2147483647;
33     dis[s]=0;
34     for(int i=1;i<=m;++i)
35       {
36           scanf("%d%d%d",&from,&to,&val);
37           tu[from].push_back(to);
38           v[from].push_back(val);
39       }
40     spfa();
41     for(int i=1;i<=n;++i)     
42        cout<<dis[i]<<" ";
43     cout<<endl;
44     return 0;
45 } 
View Code

  模板题。

  spfa还有一个应用是判负环,如果存在负边权回路,那么这个回路上的点会一直被更新,此时只要记录每一个点入队的次数,如果超过了n次,那么是一定存在负边权回路的。当然也有dfs的写法,每次如果可以更新就顺着这个点dfs下去,如果dfs到了一个已经dfs过的点,那么这个点属于一个环,并且是负环。

  例题:https://www.luogu.org/problemnew/show/P3385

  这题貌似会卡bfs版的spfa,所以放上dfs的代码好了

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 const int maxn=200005;
 6 int t; 
 7 int n,m;
 8 struct zhw{
 9     int to,last,val;
10 }tu[maxn<<1];
11 int head[maxn],tot=0;
12 int x,y,v;
13 void add(int x,int y,int v)
14 {
15     tot++;
16     tu[tot].last=head[x],head[x]=tot,tu[tot].to=y,tu[tot].val=v;
17 }
18 bool flag;
19 bool vis[maxn];
20 int dis[maxn];
21 void dfs(int x)
22 {
23     vis[x]=1;
24     for(int i=head[x];i;i=tu[i].last)
25         if(dis[tu[i].to]>dis[x]+tu[i].val)
26         {
27             if(vis[tu[i].to]||flag)
28             {
29                 flag=1;break; 
30             }
31             dis[tu[i].to]=dis[x]+tu[i].val;dfs(tu[i].to);
32         }
33     vis[x]=0;
34 }
35 int main()
36 {
37     scanf("%d",&t);
38     while(t--)
39     {
40         scanf("%d%d",&n,&m);
41         memset(head,0,sizeof(head)),tot=0,flag=0;
42         for(int i=1;i<=m;++i)
43         {
44             scanf("%d%d%d",&x,&y,&v);
45             if(v>0)add(x,y,v),add(y,x,v);
46             else
47             add(x,y,v);
48         }
49         for(int i=1;i<=n;++i)
50         {
51             dfs(i);
52             if(flag)break;
53         }
54         if(flag)printf("YE5\n");
55         else printf("N0\n");
56     }
57 }
View Code

转载于:https://www.cnblogs.com/yuelian/p/8743033.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值