【算法系列学习】SPFA邻接表最短路 [kuangbin带你飞]专题四 最短路练习 F

https://vjudge.net/contest/66569#problem/F

题意:判断图中是否存在负权回路

首先,介绍图的邻接表存储方式

数据结构:图的存储结构之邻接表

邻接表建图,类似于头插法建单链表

head[x]:以x为源点的第一条边,初始值为-1.

struct edge

{

  int to;

  int weight;

  int next;
}e[maxn];

to表示被指向的点;weight表示这条边的权重;next表示源点同为x的下一条边,这是遍历以x为源点的的关键

SPFA算法中的队列与BFS不同的是,每个点都可以在重复进入队列,而且进入队列总次数大于顶点总数说明该图存在负环。这是因为每个点的估计最短路可能在出队列后被更新,这样这个点就可以再次进入队列去更新其他点。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<string>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<queue>
  8 using namespace std;
  9 const int maxn=5205;
 10 const int inf=0x3f3f3f3f;
 11 int n,m,w;
 12 struct edge
 13 {
 14     int to;
 15     int time;
 16     int next;
 17 }e[maxn];
 18 int head[505];
 19 
 20 int Spfa(int src)
 21 {
 22     //记录每个顶点到src的距离,除了src,其余点都初始化为无穷大 
 23     int dis[505];
 24     memset(dis,inf,sizeof(dis));
 25     dis[src]=0;
 26     //记录每个顶点进入队列的总次数,大于n说明有负环 
 27     int cnt[505];
 28     memset(cnt,0,sizeof(cnt));
 29     //记录是否在队列中 
 30     bool inque[505];
 31     memset(inque,0,sizeof(inque));
 32     queue<int> Q;
 33     //源点进如队列 
 34     Q.push(src);
 35     inque[src]=1;
 36     cnt[src]++;
 37     while(!Q.empty())
 38     {
 39         int q=Q.front();
 40         Q.pop();
 41         //记录已经出队列 
 42         inque[q]=0;
 43         //邻接表,i表示边 
 44         for(int i=head[q];i!=-1;i=e[i].next)
 45         {
 46             //对每个点进行松弛,逐渐逼近最小值 
 47             if(dis[q]+e[i].time<dis[e[i].to])
 48             {
 49                 dis[e[i].to]=dis[q]+e[i].time;
 50                 //如果更新成功而且当前不在队列中,进入队列且总次数加1 
 51                 if(!inque[e[i].to])
 52                 {
 53                     inque[e[i].to]=1;
 54                     cnt[e[i].to]++;
 55                     Q.push(e[i].to);
 56                     //说明存在负环 
 57                     if(cnt[e[i].to]>n)
 58                     {
 59                         return 1;
 60                     }
 61                 }
 62             }
 63         }
 64     }
 65     return 0;
 66 }
 67 int main()
 68 {
 69     int T;
 70     scanf("%d",&T);
 71     int x,y,t;
 72     while(T--)
 73     {
 74         memset(head,-1,sizeof(head));
 75         scanf("%d%d%d",&n,&m,&w);
 76         int tot=0;
 77         for(int i=1;i<=m;i++)
 78         {
 79             scanf("%d%d%d",&x,&y,&t);
 80             e[tot].to=y;
 81             e[tot].time=t;
 82             e[tot].next=head[x];
 83             head[x]=tot++;
 84             e[tot].to=x;
 85             e[tot].time=t;
 86             e[tot].next=head[y];
 87             head[y]=tot++;
 88         }
 89         for(int i=1;i<=w;i++)
 90         {
 91             scanf("%d%d%d",&x,&y,&t);
 92             e[tot].to=y;
 93             e[tot].time=-t;
 94             e[tot].next=head[x];
 95             head[x]=tot++;
 96         }
 97         int ans=Spfa(1);
 98         if(ans==1)
 99         {
100             puts("YES");
101         }
102         else
103         {
104             puts("NO");
105         }
106     }
107     return 0;
108  } 
邻接表+SPFA

理论上以任意一点为源点都是可以的,顶点的数据范围是1~N,所以Spfa(1)或Spfa(n)都可以AC,其他的具体值不可以

原文地址:https://www.cnblogs.com/itcsl/p/6680143.html