[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}\times 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\n",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("----\n");
//    for(int i=1;i<=m;i++) printf("%d %d %d %d\n",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);
}
posted @ 2021-06-04 10:20  Kreap  阅读(44)  评论(0编辑  收藏  举报