zoukankan      html  css  js  c++  java
  • 【模板】Johnson 全源最短路

    https://www.zybuluo.com/ysner/note/1744553

    题面

    戳我

    思考

    首先来区分一下(Dijkstra)(SPFA)
    (Dij)开局不标(vis=1)
    (Dij)用某点更新的条件是(vis=0)
    (SPFA)中的(vis)表示点是否在队列中。

    //SPFA
        queue<int>Q;
    	memset(d,63,sizeof(d));d[0]=0;vis[0]=1;Q.push(0);
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].nxt)
    		{
    		  int v=e[i].to;
    		  if(d[v]>d[u]+e[i].w)
    		  {
    			a[v]=a[u]+1;
    			if(a[v]>n) return 1;//a代表最短路径所含点数,显然不能超过n
    			d[v]=d[u]+e[i].w;
    			if(!vis[v]) vis[v]=1,Q.push(v);
    		  }
    	    }
    		vis[u]=0;//
    	}
    
    //Dijkstra
    priority_queue<node>Q;
        for(int i=1;i<=n;i++) dis[i]=1e9,vis[i]=0;
        Q.push((node){s,0});dis[s]=0;
        while(!Q.empty())
        {
          int u=Q.top().u;Q.pop();
          if(vis[u]) continue;vis[u]=1;//
          for(int i=h[u];i;i=e[i].nxt)
          {
          	int v=e[i].to;
          	if(dis[v]>dis[u]+e[i].w)
          	{
          		dis[v]=dis[u]+e[i].w;
          		if(!vis[v]) Q.push((node){v,dis[v]});
          	}
          }
        }
    

    然后(Dij)不能用于负权图,故只能把图修成正权的。
    怎么办?利用(dis[v]<=dis[u]+e[i].w),我们可以把边权改成(e[i].w+dis[u]-dis[v]),而这个一定正。
    走任意一条路后,可以发现真实距离就是(sum dis+dis[v]-dis[u])(v)为终点,(u)为起点)。因为中途点的(dis)一正一负抵消掉了。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N=3e3+3;
    struct Edge{int to,nxt,w;}e[N<<2];
    struct node{int u,dis;bool operator < (const node &o) const{return dis>o.dis;}};
    int n,m,h[N],a[N],cnt;
    ll dis[N],d[N],sum;
    bool vis[N];
    int gi()
    {
    	int x=0,t=1;
    	char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    	if(ch=='-') t=-1,ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    	return x*t;
    }
    void add(int u,int v,int w){e[++cnt]=(Edge){v,h[u],w};h[u]=cnt;}
    bool SPFA()
    {
    	queue<int>Q;
    	memset(d,63,sizeof(d));d[0]=0;vis[0]=1;Q.push(0);
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].nxt)
    		{
    		  int v=e[i].to;
    		  if(d[v]>d[u]+e[i].w)
    		  {
    			a[v]=a[u]+1;
    			if(a[v]>n) return 1;
    			d[v]=d[u]+e[i].w;
    			if(!vis[v]) vis[v]=1,Q.push(v);
    		  }
    	    }
    		vis[u]=0;
    	}
    	return 0;
    }
    void Dijstra(int s)
    {
        priority_queue<node>Q;
        for(int i=1;i<=n;i++) dis[i]=1e9,vis[i]=0;
        Q.push((node){s,0});dis[s]=0;
        while(!Q.empty())
        {
          int u=Q.top().u;Q.pop();
          if(vis[u]) continue;vis[u]=1;
          for(int i=h[u];i;i=e[i].nxt)
          {
          	int v=e[i].to;
          	if(dis[v]>dis[u]+e[i].w)
          	{
          		dis[v]=dis[u]+e[i].w;
          		if(!vis[v]) Q.push((node){v,dis[v]});
          	}
          }
        }
        for(int i=1;i<=n;i++) 
          if(dis[i]==1e9) sum+=i*dis[i];
          else sum+=i*(dis[i]+d[i]-d[s]);
        printf("%lld
    ",sum);sum=0;
    }
    int main()
    {
    	n=gi();m=gi();
    	while(m--)
    	{
    		int u=gi(),v=gi(),w=gi();
    		add(u,v,w);
    	}
    	for(int i=1;i<=n;i++) add(0,i,0);
    	if(SPFA()) {puts("-1");return 0;}
    	for(int u=1;u<=n;u++)
    	  for(int i=h[u];i;i=e[i].nxt)
    	    e[i].w+=(d[u]-d[e[i].to]);
    	for(int i=1;i<=n;i++)
    	  Dijstra(i);
    	return 0;
    }
    
  • 相关阅读:
    Python3-2020-测试开发-7- 元组tuple
    Python3-2020-测试开发-6- 列表list
    面向对象三大特性之多态、封装代码注释部分
    抽象类和接口类代码注释部分
    面向对象三大特性:继承,多态,封装之继承代码注释部分
    面向对象之类的组合代码注释部分
    面对想三大特性之多态,封装
    面向对象三大特性:继承,多态,封装之继承
    类与对象的命名空间
    面向对象和类
  • 原文地址:https://www.cnblogs.com/yanshannan/p/13721030.html
Copyright © 2011-2022 走看看