• [APIO2017]商旅


    Description

    一个 (n) 个点 (m) 条边的有向图。共有 (K) 种物品,每个点都有这 (K) 种物品,并且每个点的每种物品都有一个买入卖出价格。一个物品可以在一个点买入后,走到另一个点时卖出,且任意时刻只能拥有一个物品。现在要找图上的一个有向环,使得在这个环上进行买卖后得到的利润和环的边的个数的比值最大,求比值。

    Solution

    这个东西和最优比例生成环很像,但不完全一样,买和卖这个过程很不好处理,因为一定要知道路径是怎样的。考虑最优比例生成环是怎么做的,我们是转为零一分数规划,然后判有没有正环。原图的边可以直接转换,变为 ((u,v,0))。仿照上述做法,抽象买和卖这个过程,将其也转换为边。假设在 (u)(x) 元买入,在 (v)(y) 元卖出,要使得比例最大,一定要走最短路,记为 (dis_{u,v}),如果二分一个 (mid) 后,就加入一条 ((u,v,y-x-dis_{u,v} imes mid))。最后判有没有正环。

    #include<stdio.h>
    #include<queue>
    #include<cassert>
    using namespace std;
    
    #define ll long long
    
    const int N=1e2+3;
    const int M=1e3+7;
    
    int n,m,K;
    ll a[N][M],b[N][M],mp[N][N],dis[N];
    
    struct E{
        int u,v,dis,val;
    }e[N*N*16];
    
    struct EE{
        int next,to;
        ll dis;
    }g[N*N*16];
    
    int head[N],cnt=0,in[N];
    bool tag[N];
    
    inline void add(int id,int to,ll dis){
        g[++cnt]=(EE){head[id],to,dis};
        head[id]=cnt;
    //    printf("%d %d %lld
    ",id,to,dis);
    }
    
    inline ll min(ll x,ll y){return x<y? x:y;}
    inline ll max(ll x,ll y){return x>y? x:y;}
    
    queue<int> Q;
    
    bool check(ll x){
        cnt=0; for(int i=1;i<=n;i++) head[i]=0;
        for(int i=1;i<=m;i++)
            add(e[i].u,e[i].v,1ll*e[i].val-e[i].dis*x);
        while(!Q.empty()) Q.pop();
        for(int I=1;I<=n;I++){
            for(int i=1;i<=n;i++)
                dis[i]=in[i]=tag[i]=0;
            Q.push(I); in[I]=tag[I]=1;
            while(!Q.empty()){
                int u=Q.front(); Q.pop(); tag[u]=0;
                for(int i=head[u];i;i=g[i].next){
                    int v=g[i].to;
                    if(dis[v]<=dis[u]+g[i].dis){
                        dis[v]=dis[u]+g[i].dis;
                        if((in[v]=in[u]+1)>n) return 1;
                        if(!tag[v]) tag[v]=1,Q.push(v);
                    }
                }
            }
        }
        return 0;
    }
    
    int main(){
        scanf("%d%d%d",&n,&m,&K);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=K;j++)
                scanf("%lld%lld",&a[i][j],&b[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i!=j) mp[i][j]=2e9;
        for(int i=1,x,y,D;i<=m;i++){
            scanf("%d%d%d",&x,&y,&D);
            e[i]=(E){x,y,D,0};
            mp[x][y]=min(mp[x][y],D);
        }
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++) mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++){
                if(mp[i][j]==2e9||i==j) continue;
                int ret=-1;
                for(int k=1;k<=K;k++)
                    if((a[i][k]!=-1)&&(b[j][k]!=-1)&&a[i][k]<b[j][k])
                        ret=max(ret,b[j][k]-a[i][k]);
            //    assert(ret>=-1);
                if(~ret) e[++m]=(E){i,j,mp[i][j],ret};
            }
    //    printf("----
    ");
    //    for(int i=1;i<=m;i++) printf("%d %d %d %d
    ",e[i].u,e[i].v,e[i].dis,e[i].val);
        ll lf=0,rf=1e9,ans=0;
        while(lf<=rf){
            ll mid=(lf+rf)>>1;
            if(check(mid)) ans=mid,lf=mid+1;
            else rf=mid-1;
        }
        printf("%lld",ans);
    }
    
  • 相关阅读:
    isalnum()方法
    index()方法
    find()方法
    expandtabs()方法
    endswith()方法
    encode()方法
    bytes.decode()方法
    count()方法
    center()方法
    capitalize()方法
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14848455.html
Copyright © 2020-2023  润新知