[南海云课堂] [DP 与最短路的关系] [最短路] [分层图] 道路与航线

posted on 2023-08-09 10:58:31 | under 题集 | source

题意

C 国有 \(n\) 个大城市和 \(m\) 条道路,每条道路连接这 \(n\) 个城市中的某两个城市。

任意两个城市之间最多只有一条道路直接相连。

这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 \(1\) 条。

C 国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。

但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。

商人阿龙来到 C 国旅游。

当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。

设 C 国 \(n\) 个城市的标号从 \(1∼n\),阿龙决定从 \(1\) 号城市出发,并最终在 \(n\) 号城市结束自己的旅行。

在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 \(n\) 个城市。

阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。

因为阿龙主要是来 C 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

现在给出 \(n\) 个城市的水晶球价格,\(m\) 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。

请你告诉阿龙,他最多能赚取多少旅费。

思路

  • 最短路

    一次贸易分为买入和卖出,考虑分两部分处理。

    枚举 \(i\),求出阿龙在 \(1\) 走向 \(i\) 时最少花 \(a_i\) 元买入,在 \(i\) 走向 \(n\) 时最多卖出 \(b_i\) 元,求 \(\max\limits_{1≤i≤n}{b_i-a_i}\) 即可。

    容易想到 \(\rm {DP}\)\(a\)\(b\),但图中可能有环,因此 \(\rm{DP}\) 存在后效性。

    在部分情况下最短路算法可替代 \(\rm{DP}\),因此当 \(\rm{DP}\) 不行时,我们考虑最短路算法。

    同上,\(\rm{Dijkstra}\) 也存在后效性,只能使用 \(\rm{SPFA}\) 处理了。

    建反边跑两次 \(SPFA\),这道题就解决了。

  • 分层图

    这道题和 T3 优惠 的分层图思想差不多。

    定义 \((u,0/1,0/1)\) 代表到达城市 \(u\) 时,是否持有水晶球,是否进行过交易。

    建边分为两类,单纯传递信息和联系不同状态:

    • 传递信息
    1. \((u,0,0)\) \(->\) \((v,0,0)\),边权为 \(0\)
    2. \((u,1,0)\) \(->\) \((v,1,0)\),边权为 \(0\)
    3. \((u,0,1)\) \(->\) \((v,0,1)\),边权为 \(0\)
    • 联系状态
    1. \((u,0,0)\) \(->\) \((u,1,0)\),边权为 \(-w_u\),表示花费 \(w_u\) 买入一个水晶球。
    2. \((u,1,0)\) \(->\) \((u,0,1)\),边权为 \(w_u\),表示卖出水晶球并获得 \(w_u\) 元。

    然后求出 \((1,0,0)\)\((n,0,1)\) 的最短路即可。

代码

  • 最短路

    此题给出的是点权而不是边权,需要将点权赋给相连的边。于是,对于 \(e(u,v)\),令 \(w(u,v)=w_v\)

    之所以不往前赋值,是因为往前赋值会忽略掉终点(和起点)的点权,处理有些麻烦。而往后赋值只需预处理源点就好了。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5,M=5e5+5,INF=0x3f3f3f3f;
    int n,m,u,v,f,w[N],head[N],head2[N],cnt,cnt2;
    int dis[N],vis[N],dis2[N],vis2[N],ans;
    struct edge{
        int v,nxt,w;
    }e[2*M],e2[2*M];
    void add(int u,int v,int w){
        e[++cnt]={v,head[u],w};
        head[u]=cnt;
    }
    void add2(int u,int v,int w){
        e2[++cnt2]={v,head2[u],w};
        head2[u]=cnt2;
    }
    void insert(int u,int v){
        add(u,v,w[u]);
        add2(v,u,w[v]);
    }
    void SPFA(int k){
        for(int i=1; i<=n;i++){
            dis[i]=114514;
        }
        queue<int>q;
        q.push(k);
        vis[k]=1;
        dis[k]=w[k];
        while(!q.empty())
        {
            k=q.front();
            q.pop();
            vis[k]=0;
            for(int i=head[k]; i;i=e[i].nxt)
            {
                int j=e[i].v;
                if(min(dis[k],e[i].w)<dis[j])
                {
                    dis[j]=min(dis[k],e[i].w);
                    if(vis[j]==0)
                    {
                        q.push(j);
                        vis[j]=1;
                    }
                }
            }
        }
    }
    void SPFA2(int k){
        for(int i=1; i<=n;i++){
            dis2[i]=-114514;
        }
        queue<int>q2;
        q2.push(k);
        vis2[k]=1;
        dis2[k]=w[k];
        while(!q2.empty())
        {
            k=q2.front();
            q2.pop();
            vis2[k]=0;
            for(int i=head2[k]; i;i=e2[i].nxt)
            {
                int j=e2[i].v;
                if(max(dis2[k],e2[i].w)>dis2[j])
                {
                    dis2[j]=max(dis2[k],e2[i].w);
                    if(vis2[j]==0)
                    {
                        q2.push(j);
                        vis2[j]=1;
                    }
                }
            }
        }
    }
    int main(){
        cin>>n>>m;
        for(int i=1; i<=n;i++){
            scanf("%d",&w[i]);
        }
        for(int i=1; i<=m;i++){
            scanf("%d%d%d",&u,&v,&f);
            if(f==2){
                insert(u,v);
                insert(v,u);
            }
            else{
                insert(u,v);
            }
        }
        SPFA(1);
        SPFA2(n);
        for(int i=1; i<=n;i++){
            ans=max(ans,dis2[i]-dis[i]);
        }
        cout<<ans;
        return 0;
    }
    
  • 分层图

    分层图真是太强啦!

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e5+5,M=4*5e5+5;
    int n,m,u,v,f,head[N],vis[N],cnt;
    int w[N],dis[N];
    queue<int>q;
    struct edge{
        int v,nxt,w;
    }e[2*M];
    void add(int u,int v,int w){
        e[++cnt].v=v;
        e[cnt].w=w;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    inline int id(int u,int p,int pp){
        return (pp+p*2)*n+u;
    }
    void insert(int u,int v){
        add(id(u,0,0),id(v,0,0),0);
        add(id(u,1,0),id(v,1,0),0);
        add(id(u,0,1),id(v,0,1),0);
    }
    void SPFA(int k){
        memset(dis,-0x3f,sizeof(dis));
        q.push(k);
        vis[k]=1;
        dis[k]=0;
        while(!q.empty())
        {
            k=q.front();
            q.pop();
            vis[k]=0;
            for(int i=head[k]; i;i=e[i].nxt)
            {
                int j=e[i].v;
                if(dis[k]+e[i].w>dis[j])
                {
                    dis[j]=dis[k]+e[i].w;
                    if(vis[j]==0)
                    {
                        q.push(j);
                        vis[j]=1;
                    }
                }
            }
        }
    }
    int main(){
        cin>>n>>m;
        for(int i=1; i<=n;i++){
            scanf("%d",&w[i]);
            add(id(i,0,0),id(i,1,0),-w[i]);
            add(id(i,1,0),id(i,0,1),w[i]);
        }
        for(int i=1; i<=m;i++){
            scanf("%d%d%d",&u,&v,&f);
            if(f==1){
                insert(u,v);
            }
            else{
                insert(u,v);
                insert(v,u);
            }
        }
        SPFA(id(1,0,0));
        if(dis[id(n,0,1)]<0){
            cout<<0;
        }
        else{
            cout<<dis[id(n,0,1)];
        }
        return 0;
    }
    
posted @ 2026-01-12 20:17  Zwi  阅读(2)  评论(0)    收藏  举报