[kuangbin帶你飛]專題四 最短路 題解+總結

kuangbin帶你飛:點擊進入新世界
最短路算法模板:點擊進入新世界

總結:

本人算是初學者中的初學者,歡迎交流~
kuangbin的專題確實是理解最短路的一大途徑,這篇博客主要記錄題解,順便總結最短路的題型。
A.模板題 參考1 . 6 . 10
B.變形最短路 (求最大邊權的最小值or最小邊權的最大值) 參考2 . 3
C.變形最短路 (求往返最短路的最大值) 參考4
D.變形最短路 (求往返最短路之和) 參考5
E.含負權邊的最短路 (spfa判負環) 參考 7 . 11 . 12
F. 確定排名 參考8
G.差分約束系統 (<=最短路 and >=最長路) 參考9 . 11
ps:跑最短路求得就是最大的距離,跑最長路求的就是最小的距離
H.判正環 參考 13 . 14
差分約束系統介紹

最短路其實不難,難點在於建圖,剩下五道題沒靈感,等國慶後補。

kuangbin之外:
最短路計數
最短路+二分
最短路+dp
分層圖最短路

1.Til the Cows Come Hom

原題鏈接:傳送門

思路:

  1. 經典最短路模板題,注意一下輸入的坑,起點爲n,終點爲1,其他沒有什麼問題。
  2. VJ不能用萬能頭!!!VJ不能用萬能頭!!!VJ不能用萬能頭!!!

    代碼如下:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
#define R register int
using namespace std;
const int manx=1e4+5;
const int mamx=1e4+5;
int head[manx],d[manx];
bool vis[manx];
int n,m,k=0,s,e;
struct node{
    int v,next,w;
}a[manx];
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    a[k].w=w;
    a[k].v=v;
    head[u]=k;
}
void dij()
{
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s]=0;
    priority_queue<pair<int,int> >q;
    q.push(make_pair(0,s));
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=a[i].next){
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
        }
    }

}
int main()
{
    scanf("%d%d",&m,&n);
    e=1,s=n;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    dij();
    cout<<d[e];
    return 0;
}


2.Frogger

原題鏈接:傳送門

思路:

  1. 變形最短路求最大邊權的最小值。
  2. 由於題目給的數據過小,可以考慮floyd,不過 d[i][j] 不在表示點i到點j 的最短路,而是點i到點j路徑中最大邊權的最小值,可以通過改變鬆弛操作實現:
  3. 原先的鬆弛操作:d[i][j]= min( d[i][j], d[i][k] + d[k][j]) ;
  4. 本題的鬆弛操作:d[i][j]= min( d[i][j] ,max( d[i][k] , d[k][j] );

AC代碼如下:

 // #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define R register int
#define mm make_pair
using namespace std;
const int manx=300;
int x[manx],y[manx];
double d[manx][manx];
int main()
{
    int n,m,f=1;
    while(scanf("%d",&n)!=EOF&&n)
    {
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)
            scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                d[i][j]=d[j][i]=sqrt(pow((x[i]-x[j])*1.0,2)+pow((y[i]-y[j])*1.0,2));
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    d[i][j]=min(d[i][j],max(d[i][k],d[k][j]));
        cout<<"Scenario #"<<f++<<endl;
        printf("Frog Distance = %.3lf\n\n",d[1][2]);
    }
    return 0;
}


3.Heavy Transportation

原題鏈接:傳送門

思路:

  1. 同上,本題爲變形最短路,但這裏求的是最小邊的最大值並且n的範圍到達了1e3,O(n^3)的時間複雜度已經不夠看了,這裏我用的是spfa。
  2. 同樣鬆弛操作需要修改一番。
  3. 原本的鬆弛操作 : d[v]= min(d[v], d[u]+w);
  4. 本題的鬆弛操作 : d[v]= max(d[v] , min(d[u], w);
  5. 這裏d[v]表示的是到達v點的所有路徑中最小邊的最大值。

AC代碼如下:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<string>
using namespace std;
const int manx=1e6+5;
const int mamx=1e6+5;
int head[manx],d[manx];
bool vis[manx];
struct node{
    int v,w,next;
}a[mamx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    a[k].w=w;
    a[k].v=v;
    head[u]=k;
}
void spfa()
{
    memset(d,0,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s]=1e9;
    queue<int>q;
    q.push(s);
    while(q.size())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[v]<min(d[u],w)) {
                    d[v]=min(d[u],w);
                    if(!vis[v])
                        q.push(v),vis[v]=1;
            }
        }
    }
}
int main()
{
    int t;
    cin>>t;
    for(int o=1;o<=t;o++)
    {
        scanf("%d%d",&n,&m);
        s=1,e=n,k=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        spfa();
        printf("Scenario #%d:\n",o);
        printf("%d\n",d[e]);
        printf("\n");
    }
    return 0;
}


4.Silver Cow Party

原題鏈接:傳送門

思路:

  1. 求所有點到源點的最短往返路程的最大值。
  2. 同理,n的範圍達到1e3,不能用floyd,不然直接 min( d[i][v]+ d[v][i]) 完事。
  3. 這裏我是用dij,對於每個點都跑一次dij,開二維數組保存每個點的最短路。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
    int v,next,w;
}a[mamx];
int d[manx][manx],head[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].v=v;
    a[k].w=w;
}
void dij()
{
    for(int i=1;i<=n;i++) d[s][i]=1e9;
    memset(vis,0,sizeof(vis));
    priority_queue<pair<int,int> >q;
    d[s][s]=0;
    q.push(make_pair(0,s));
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[s][v]>d[s][u]+w)
                d[s][v]=d[s][u]+w,q.push(make_pair(-d[s][v],v));
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&e);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1;i<=n;i++) s=i,dij();
    int ans=-1;
    for(int i=1;i<=n;i++){
        if(i==e) continue;
        if(ans<d[i][e]+d[e][i])
            ans=d[i][e]+d[e][i];
    }
    cout<<ans<<endl;
    return 0;
}


5.Invitation Cards

原題鏈接:傳送門

思路:

  1. 依然是求往返路程,但這道題求的是各節點到源點的往返路程之和,而且這道題的n範圍更離譜,導致這裏不能用鄰接表存邊。
  2. 所以這道題應該正向和反向存圖,然後dij跑兩遍,求相加求和即可。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1000000+5;
const int mamx=manx;
struct node{
    int v,next,w;
}a[2][mamx];
int d[2][manx],head[2][manx];
bool vis[manx];
int n,m,s,e,k0=0,k1=0;
void add(int i,int u,int v,int w)
{
    int k;
    if(i==0) k=k0; else k=k1;
    a[i][++k].next=head[i][u];
    head[i][u]=k;
    a[i][k].v=v;
    a[i][k].w=w;
    if(i==0) k0=k; else k1=k;
}
void dij(int x)
{
    for(int i=1;i<=n;i++) d[x][i]=1e9;
    memset(vis,0,sizeof(vis));
    priority_queue<pair<int,int> >q;
    d[x][1]=0;
    q.push(make_pair(0,1));
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[x][u];i;i=a[x][i].next)
        {
            int v=a[x][i].v,w=a[x][i].w;
            if(d[x][v]>d[x][u]+w)
                d[x][v]=d[x][u]+w,q.push(make_pair(-d[x][v],v));
        }
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        k0=0,k1=0;
        memset(head,0,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(0,u,v,w);
            add(1,v,u,w);
        }
        dij(0),dij(1);
        long long ans=0;
        for(int i=2;i<=n;i++){
            ans+=d[0][i]+d[1][i];
        }
        cout<<ans<<endl;
    }
    return 0;
}



6.Tram

原題鏈接:傳送門

思路:

  1. 這道題可以說是模板題,主要是題意理解,然後按題意建邊即可。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e5+5;
int head[manx],d[manx];
bool vis[manx];
struct node{
    int v,w,next;
}a[mamx];
priority_queue<pair<int,int> >q;
int k=0,n,m,s,e;
void add(int u,int v ,int w)
{
    a[++k].next=head[u];
    a[k].v=v;
    a[k].w=w;
    head[u]=k;
}
void dij()
{
    for(int i=1;i<=n;i++) d[i]=1e9;
    d[s]=0;
    q.push(make_pair(0,s));
    while(q.size())
    {
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w&&!vis[v]) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&s,&e);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&m);
        for(int j=1;j<=m;j++)
        {
            int v;
            scanf("%d",&v);
            if(j==1) add(i,v,0);
            else add(i,v,1);
        }
    }
    dij();
    if(d[e]==1e9) cout<<"-1"<<endl;
    else cout<<d[e]<<endl;
    return 0;
}


7.Wormholes

原題鏈接:傳送門

思路:

  1. spfa判定負環的模板題,只要有負環存在,那麼負環會越縮越小,即時間無限回溯,即可以回到出發之前。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
    int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].v=v;
    a[k].w=w;
}
bool spfa()
{
    for(int i=1;i<=n;i++) d[i]=1e9,f[i]=0;
    memset(vis,0,sizeof(vis));
    queue<int>q;
    d[1]=0;
    f[1]=1;
    q.push(1);
    while(q.size()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!vis[v]){
                    f[v]++;
                    if(f[v]>=n) return false;
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    return true;
}
int main()
{
    int o;
    cin>>o;
    while(o--){
        memset(head,0,sizeof(head));
        scanf("%d%d%d",&n,&m,&e);
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        for(int i=1;i<=e;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,-w);
        }
        if(spfa()) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
    return 0;
}


8.Cow Contest

原題鏈接:傳送門

思路:

  1. 給定u,v即u打敗v,求明確排名的點的個數。
  2. 像這種數據範圍小於1e3的題目看到第一反應就要想到floyd,即對所給邊確定 d[u][v]=1,然後通過鬆弛操作 d[i][j] = ( d[i][k] && d[k][j] ) ? 1 : 0;
  3. 然後對n個點進行遍歷,如果這個點跟其他n-1個點都確定關係,即這個點的排名是確定的。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e2+5;
const int mamx=4e5+5;
struct node{
    int v,next,w;
}a[mamx];
int d[manx][manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].v=v;
    a[k].w=w;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        d[u][v]=1;
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(d[i][k]&&d[k][j])
                    d[i][j]=1;
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int res=0;
        for(int j=1;j<=n;j++)
            if(d[i][j]||d[j][i]) res++;
        if(res==n-1) ans++;
    }
    cout<<ans<<endl;
    return 0;
}


9.Candies

原題鏈接:傳送門

思路:

  1. 給定各種約束條件,求最大差異。
  2. 由於約束條件中出現<= ,即求最短路 ,理解後就是模板題。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=3e5+5;;
const int mamx=3e6+5;
int head[manx],d[manx];
bool vis[manx];
int n,m,k=0,s,e;
struct node{
    int v,next,w;
}a[manx];
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    a[k].w=w;
    a[k].v=v;
    head[u]=k;
}
void dij()
{
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s]=0;
    priority_queue<pair<int,int> >q;
    q.push(make_pair(0,s));
    while(q.size()){
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=a[i].next){
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w) d[v]=d[u]+w,q.push(make_pair(-d[v],v));
        }
    }

}
int main()
{
    scanf("%d%d",&n,&m);
    e=n,s=1;
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
    }
    dij();
    cout<<d[e];
    return 0;
}



10.MPI Maelstrom

原題鏈接:傳送門

思路:

  1. n的範圍是1e2級別的,可以考慮floyd,模板題。
  2. atoi(s) 是STL的一個函數,可以把一個字符數組s轉化成整型數字。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f
using namespace std;
const int manx=300;
int d[manx][manx];
int main()
{
    int n,m;
    char s[12];
    while(scanf("%d",&n)!=EOF&&n)
    {
        memset(d,inf,sizeof(d));
        for(int i=1;i<=n;i++) d[i][i]=0;
        for(int i=2;i<=n;i++)
            for(int j=1;j<i;j++)
            {
                cin>>s;
                if(s[0]=='x') continue;
                d[i][j]=d[j][i]=atoi(s);
            }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                        d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        int ans=-1;
        for(int i=2;i<=n;i++) if(d[1][i]!=inf) ans=max(ans,d[1][i]);
        cout<<ans<<endl;
    }
    return 0;
}


11.Layout

原題鏈接:傳送門

思路:

  1. 這道題可以說是差分約束系統和判斷負環的結合,可以先嚐試先做本博客的7和9題再來做這道題。
  2. 對於 A-B<=X; C-D>=Y;
  3. 把第二個式子 *(-1)即:C-D>=y -> D-C<=-y 這樣兩個式子都是小於等於方便求最短路。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define R register int
#define mm make_pair
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
    int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].v=v;
    a[k].w=w;
}
void spfa()
{
    for(int i=1;i<=n;i++) d[i]=1e9,f[i]=0;
    memset(vis,0,sizeof(vis));
    queue<int>q;
    d[1]=0;
    f[1]=1;
    q.push(1);
    while(q.size()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!vis[v]){
                    f[v]++;
                    if(f[v]>n){
                        cout<<"-1"<<endl;
                        return;
                    }
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
    if(d[n]==1e9) cout<<"-2"<<endl;
    else cout<<d[n]<<endl;
    return ;
}
int main()
{
    scanf("%d%d%d",&n,&m,&e);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    for(int i=1;i<=e;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(v,u,-w);
    }
    spfa();
    return 0;
}


12.Extended Traffic

原題鏈接:傳送門

思路:

  1. 這道題可以說spfa判負環的模板題。
  2. 但是有個坑點是用pow函數必須用(int)強轉,不然會wa ,本人實測wa5發 卡了半小時才發現是這裏出錯了。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3+5;
const int mamx=1e6+5;
struct node{
    int v,next,w;
}a[mamx];
int d[manx],head[manx],f[manx],ff[manx];
bool vis[manx];
int n,m,s,e,k=0;
void add(int u,int v,int w)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].v=v;
    a[k].w=w;
}
void spfa()
{
    memset(ff,0,sizeof(ff));
    memset(d,inf,sizeof(d));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    d[1]=0;
    ff[1]=1;
    q.push(1);
    while(q.size()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v,w=a[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!vis[v]&&ff[v]<=n){ //當ff[v]入隊次數不大於n才進入if裏面
                    ff[v]++;
                    q.push(v);
                    vis[v]=1;
                }
            }
        }
    }
}
int main()
{
    int o,oo=1;
    cin>>o;
    while(o--)
    {
        memset(head,0,sizeof(head));
        k=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&f[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d",&u,&v);
            w=int(pow(f[v]-f[u],3));
            add(u,v,w);
        }
        spfa();
        scanf("%d",&s);
        printf("Case %d:\n",oo++);
        while(s--)
        {
            scanf("%d",&e);
            if(ff[e]>n||d[e]<3||d[e]==inf) printf("?\n");
            else printf("%d\n",d[e]);
        }
    }
    return 0;
}


13.Currency Exchange

原題鏈接:傳送門

思路:

  1. 一種貨幣就是一個點,一個“兌換點”就是圖上兩種貨幣之間的一個兌換方式,求貨幣是否增值就是求圖中是否存在正環,即跟求負環條件相反即可,具體代碼見。

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=300;
const int mamx=300;
double d[manx];
int f[manx],head[manx];
bool vis[manx];
int n,m,s,k=0;
double g;
struct node{
    int v,next;
    double f1,f2;
}a[mamx];
void add(int u,int v,double f1,double f2)
{
    a[++k].next=head[u];
    head[u]=k;
    a[k].f1=f1;
    a[k].f2=f2;
    a[k].v=v;
}
bool spfa()
{
    memset(d,0,sizeof(d));
    memset(f,0,sizeof(f));
    memset(vis,0,sizeof(vis));
    d[s]=g;
    queue<int>q;
    q.push(s);
    f[s]++;
    while(q.size())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].v;
            double val=(d[u]-a[i].f2)*a[i].f1;
            if(d[v]<val){
                d[v]=val;
                if(!vis[v]){
                    vis[v]=1;
                    f[v]++;
                    q.push(v);
                    if(f[v]>n) return 1;
                }
            }
        }
    }
    if(d[s]>g) return 1;  //即便沒有正環 看跑完起點的貨幣是否增值
    return 0;
}
int main()
{
    while(scanf("%d%d%d%lf",&n,&m,&s,&g)!=EOF)
    {
        k=0;
        memset(head,0,sizeof(head));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            double x,y;
            scanf("%d%d",&u,&v);
            scanf("%lf%lf",&x,&y);
            add(u,v,x,y);
            scanf("%lf%lf",&x,&y);
            add(v,u,x,y);
        }
        if(spfa()) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
}


14.Arbitrage

原題鏈接:傳送門

思路:

  1. 跟13題一樣是貨幣兌換的問題,依然求正環,由於這道題的數據規模不大,考慮floyd,在輸入的時候把數據處理好即可。
  2. 原本鬆弛操作: d[i][j]= min( d[i][j], d[i][k] + d[k][j])
  3. 本題鬆弛操作: d[i][j]= max( d[i][j], d[i][k] * d[k][j])

AC代碼如下:

// #include<bits/stdc++.h>
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<stack>
#define R register int
#define mm make_pair
#define inf 0x3f3f3f3f
using namespace std;
const int manx=100;
int n,m;
string s;
map<string,int>q;
double d[manx][manx];
int main()
{
    int haha=1;
    while(scanf("%d",&n)!=EOF&&n)
    {
        memset(d,0.0,sizeof(d));
        q.clear();
        for(int i=1;i<=n;i++) d[i][i]=1.0;
        for(int i=1;i<=n;i++) cin>>s,q[s]=i;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            string u,v;
            double w;
            cin>>u>>w>>v;
            int x,y;
            x=q[u],y=q[v];
            d[x][y]=max(d[x][y],w);
        }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    d[i][j]=max(d[i][j],d[i][k]*d[k][j]);
        int flag=1;
        for(int i=1;i<=n;i++)
            if(d[i][i]>1)
                flag=0;
        if(!flag) printf("Case %d: Yes\n",haha++);
        else printf("Case %d: No\n",haha++);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章