[kuangbin带你飞]专题四【最短路练习】(spfa)

预备知识:链式前向星存图

推荐blog

spfa模板

queue<int>q;
    For(i,1,n)dis[i]=INF,vis[i]=0;
    dis[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u]; i!=-1; i=e[i].next)
        {
            int v=e[i].to,w=e[i].v;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!vis[v])
                {
                    vis[v]=1;//避免重复入队
                    q.push(v);
                }
            }
        }
    }

建议做题顺序(spfa) :

(10) 14 12 13 5(6)15 18

(编号10博主放在了dij专题里)

Currency Exchange(poj1860)

正权回路

picture
在这里插入图片描述

Sample Input

3 2 1 20.0
1 2 1.00 1.00 1.00 1.00
2 3 1.10 1.00 1.10 1.00

Sample Output

YES

思路:

本题要求找到一个正权回路

总结

  1. 正权回路用最长路,负权回路用最短路。
    (dis[e]既然要无限增加,那么更新条件应该是dis[e]<x,负权路反之,)
  2. dij可以解决正权回路,但不适合解决负权回路。

AC(spfa)

模板

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x);i<=(y); i++)
#define fzhead EDGE(int _to, double _r, double _c, int _next)
#define fzbody to(_to),r(_r),c(_c), next(_next)
using namespace std;
const int maxn=1e2+10;
const int maxm=2e2+10;
struct EDGE
{
    int to,next;
    double c,r;
    EDGE() {}
fzhead:
    fzbody{}
} e[maxm];
double dis[maxn],v;
int vis[maxn],head[maxn];
int cnt,n,m,s;
void add(int bg, int ed, double r,double c)
{
    e[++cnt]=EDGE(ed,r,c,head[bg]);
    head[bg]=cnt;
}
bool spfa(int n, int s, double val)
{
    queue<int>q;
    For(i,1,n)vis[i]=0,dis[i]=-1;
    dis[s]=val;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i!=-1; i=e[i].next)
        {
            int v=e[i].to;
            double x,y;
            x=e[i].c,y=e[i].r;
            if(dis[v]<(dis[u]-x)*y)
            {
                dis[v]=(dis[u]-x)*y;
                q.push(v);
                vis[v]++;
                if(vis[v]>n+1)return 1;//形成正负权回路的条件,大牛们总结得出,具体证明我也不会
            }
        }
    }
    if(vis[s]-val>0)return 1;//这个地方是比较容易忽略的,一般来说根据题目而定。
    return 0;
}
int main()
{
    scanf("%d%d%d%lf",&n,&m,&s,&v);
    mst(head,-1);
    For(i,1,m)
    {
        int x,y;
        double c1,c2,r1,r2;
        scanf("%d%d%lf%lf%lf%lf",&x,&y,&r1,&c1,&r2,&c2);
        add(x,y,r1,c1);
        add(y,x,r2,c2);
    }
    int flag=spfa(n,s,v);
    printf("%s\n",(flag)?"YES":"NO");
    return 0;
}

Wormholes(poj3295)(负权回路)

spfa虫洞,(当然floyd也可,不过floyd复杂度有点大)

在这里插入图片描述
在这里插入图片描述

Sample Input

2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8

Sample Output

NO
YES

Hint

  1. For farm 1, FJ cannot travel back in time.
  2. For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.

题意:

本题要求可以时光倒流,即找到一个负圈

AC(spfa)

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
#define fzhead EDGE(int _to, int _v, int _next)
#define fzbody to(_to),  v(_v), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
using namespace std;
const int maxn=5e2+10;
const int maxm=1e4;
const int INF=0x3f3f3f3f;
struct EDGE
{
    int to,next,v;
    EDGE(){}
    fzhead:fzbody{}
}e[maxm];
int cnt,n,m,w;
int dis[maxn],head[maxn],vis[maxn];
void add(int bg, int ed, int v)
{
    e[++cnt]=EDGE(ed,v,head[bg]);
    head[bg]=cnt;
}
bool spfa(int n)
{
    For(i,1,n)dis[i]=INF,vis[i]=0;
    queue<int>q;
    dis[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u]; i!=-1; i=e[i].next)
        {
            int v=e[i].to,w=e[i].v;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                q.push(v);
                vis[v]++;
                if(vis[v]>n+1)return 1;//已经形成负圈
            }
        }
    }
    return 0;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        cnt=0;
        mst(head,-1);
        scanf("%d%d%d",&n,&m,&w);
        For(i,1,m)
        {
            int x,y,v;
            scanf("%d%d%d",&x,&y,&v);
            add(x,y,v);
            add(y,x,v);
        }
        For(i,1,w)
        {
            int x,y,v;
            scanf("%d%d%d",&x,&y,&v);
            add(x,y,-v);
        }
        int flag=spfa(n);
        printf("%s\n",flag?"YES":"NO");
    }
    return 0;
}

AC(Floyd)

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int MAXN=500+10;
const int INF=0x3f3f3f3f;
int g[MAXN][MAXN];
//int b[MAXN][MAXN];
int n,m,w;
int floyd()
{
    int i,j,k,flag =0;
    for( k=1; k<=n; k++)
    {
        for( i=1; i<=n; i++)
        {
            for( j=1; j<=n; j++)
            {
                if(g[i][j]>g[i][k]+g[k][j])g[i][j]=g[i][k]+g[k][j];
                
               // cout<<g[i][i]<<endl;
            }
            if(g[i][i]<0)
            {
                flag=1;
                break;
            }
        }
        if(flag)
            {
                flag=1;
                break;
            }
    }
  //  int ans=0;
    if(flag)cout<<"YES"<<endl;
    else cout<<"NO"<<endl;
}
//
int main()
{
    //ios::sync_with_stdio(false);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int x,y;
        int z;
       // memset(g,0x3f,sizeof(g));
       // memset(b,0x3f,sizeof(b));
        scanf("%d%d%d", &n, &m,&w);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
        {
            if(i==j)g[i][j]=0;
            else g[i][j]=INF;
        }
      //  for(int i=1; i<=n; i++)
        //    g[i][i]=0;
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            if(z<g[x][y])g[x][y]=g[y][x]=z;
        }
        for(int i=1; i<=w; i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            g[x][y]=-z;
        }
        floyd();
        //solve();
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章