[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章