• 最优贸易


    链接

    https://www.acwing.com/problem/content/343/

    题目

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

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

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

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

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

    商人阿龙来到C国旅游。

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

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

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

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

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

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

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

    注意:本题数据有加强。

    输入格式
    第一行包含 2 个正整数 n 和 m,中间用一个空格隔开,分别表示城市的数目和道路的数目。

    第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。

    接下来 m 行,每行有 3 个正整数,x,y,z,每两个整数之间用一个空格隔开。

    如果z=1,表示这条道路是城市 x 到城市 y 之间的单向道路;如果z=2,表示这条道路为城市 x 和城市 y 之间的双向道路。

    输出格式
    一个整数,表示答案。

    数据范围
    1≤n≤100000,
    1≤m≤500000,
    1≤各城市水晶球价格≤100
    输入样例:

    5 5
    4 3 5 6 1
    1 2 1
    1 4 1
    2 3 2
    3 5 1
    4 5 2
    

    输出样例:

    5
    

    思路

    两种做法:

    tarjan缩点+拓扑DP

    将可以相互到达的强连通子图缩块,记录每个块内的最高和最低价格,对于块之间一定由单向边构成且无环。则对图做拓扑DP,记录每个块以及可以到达它的块的最低价格,通过枚举在每个块内卖出获得的最大价值更新答案。
    拓扑DP的过程需要记录这个块是否可以由1点达到,如果1号点不能到达某个块,那么也不能在这个块内买入或卖出。

    代码

    #include<iostream>
    #include<string.h>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<string>
    #include<map>
    #include<stack>
    using namespace std;
    const int N=1e5+7,M=1e6+7,INF=0x3f3f3f3f;
    int Stack[N], low[N],stackk[N], dfn[N], inStack[N], belong[N],h[N];
    int now, cnt,tot,rt; // now:时间戳,cnt强连通的个数
    struct edge{
        int v,nex;
    }edges[M],e[M];
    int head[N],edge_cnt,top;
    void addedge(int u,int v){
        edges[++cnt]=(edge){v,head[u]};
        head[u]=cnt;
    }
    stack<int> s;
    map<int,map<int,int> >mmp;
    int price[N],Min[N],Max[N],in[N],ans[N];
    void init() {
        now = cnt =tot =rt=  0;
        memset(head,-1,sizeof head);
        memset(h,-1,sizeof h);
        memset(Min,0x3f,sizeof Min);
        memset(Max,-0x3f,sizeof Max);
    }
    void tarjan(int x){
        dfn[x]=low[x]=++now;
        inStack[x]=1;stackk[++top]=x;
        for(int i=head[x];i;i=edges[i].nex){
            int v=edges[i].v;
            if(!dfn[v]){
                tarjan(v);
                low[x]=min(low[x],low[v]);
            }
            else if(inStack[v])low[x]=min(low[x],dfn[v]);
        }
        if(low[x]==dfn[x]){
            rt++;
            Max[cnt]=-INF;Min[cnt]=INF;
            int t;
            do{
                t=stackk[top--];
                Max[rt]=max(Max[rt],price[t]);
                Min[rt]=min(Min[rt],price[t]);
                inStack[t]=0;
                belong[t]=rt;
            }while(x!=t);
        }
    }
    void ad(int u,int v){
        e[++tot]=(edge){v,h[u]};
        h[u]=tot;
    }
    int reach[N];
    int main(){
        int n,m;
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&price[i]);
        }
        for(int i=1;i<=m;++i){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(z==1){
                addedge(x,y);
            }
            else {
                addedge(x,y);
                addedge(y,x);
            }
        }
        for(int i=1;i<=n;++i){
            if(!dfn[i])
                tarjan(i);
        }
        for(int i=1;i<=n;++i){
            int u=belong[i];
            for(int j=head[i];~j;j=edges[j].nex){
                int v=belong[edges[j].v];
                if(u==v||mmp[u][v]) continue;
                ad(u,v);
                in[v]++;
                mmp[u][v]=1;
            }
        }
        queue<int> q;
        reach[belong[1]]=1;
        for(int i=1;i<=rt;++i){
            if(in[i]==0) q.push(i);
        }
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=h[u];~i;i=e[i].nex){
                int v=e[i].v;
                if(reach[u]){
                    reach[v]=1;
                    ans[v]=max(ans[v],ans[u]);
                    Min[v]=min(Min[u],Min[v]);
                    ans[v]=max(ans[v],Max[v]-Min[v]);
                }
                in[v]--;
                if(!in[v]) q.push(v);
            }
        }
        cout<<ans[belong[n]];
        return 0;
    }
    

    分层图

    从1号点出发跑spfa,记录每个点以及可以到达他的点买入的最低价格。再从n号点跑反向边spfa,记录每个点以及可以到达他的点卖出的最高价格。最后枚举每个点更新答案。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100010,M=500010 ;
    int mn[N],mx[N],price[N],inq[N];
    int hs[N],ht[N],tot1,tot2;
    struct eg{
        int v,nex;
    }es[M*2],et[M*2];
    void ads(int u,int v){
        es[++tot1]=(eg){v,hs[u]};
        hs[u]=tot1;
    }
    void adt(int u,int v){
        et[++tot2]=(eg){v,ht[u]};
        ht[u]=tot2;
    }
    void spfa(int s,int d[],int type,int h[],eg e[] ){
        queue<int> q;
        memset(inq,0,sizeof inq);
        q.push(s);inq[s]=1;
        d[s]=price[s];
        while(!q.empty()){
            int u=q.front();q.pop();
            inq[u]=0;
            for(int i=h[u];~i;i=e[i].nex){
                int v=e[i].v;
                if((type==1&&d[v]>min(d[u],price[v]))||(type==2&&d[v]<max(price[v],d[u]))   ){
                    if(type==1) d[v]=min(d[u],price[v]);
                    if(type==2) d[v]=max(price[v],d[u]);
                    if(!inq[v]){
                        inq[v]=1;
                        q.push(v);
                    }
                }
            }
        }
    }
    int main(){
        int n,m;
        memset(hs,-1,sizeof hs);
        memset(ht,-1,sizeof ht);
        memset(mx,-0x3f,sizeof mx);
        memset(mn,0x3f,sizeof mn);
        cin>>n>>m;
        for(int i=1;i<=n;++i) cin>>price[i];
        while(m--){
            int u,v,type;
            cin>>u>>v>>type;
            if(type==1){
                ads(u,v);
                adt(v,u);
            }
            else {
                ads(u,v);
                ads(v,u);
                adt(v,u);
                adt(u,v);
            }
        }
        spfa(1,mn,1,hs,es);
        spfa(n,mx,2,ht,et);
        int res=0;
        for(int i=1;i<=n;++i) {
            res=max(res,mx[i]-mn[i]);
        }
        cout<<res<<endl;
        return 0;
    }
    
  • 相关阅读:
    Android SwipeActionAdapter结合Pinnedheaderlistview实现复杂列表的左右滑动操作
    Android 使用SwipeActionAdapter开源库实现简单列表的左右滑动操作
    你读到了什么:谈谈阅读的空与实
    飞行的架构师和奔跑的程序员
    poj 3252 数位dp
    hdu 4734 数位dp
    hdu 2089 不要62 数位dp入门
    蓝桥杯模拟赛 青出于蓝而胜于蓝
    bzoj 4034
    hdu 3974 dfs时间戳+线段树
  • 原文地址:https://www.cnblogs.com/jjl0229/p/12753796.html
Copyright © 2020-2023  润新知