[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;
}


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