[APIO2017] 商旅

题意:

有一张n个点m条边的有向图,走每条边需要花费时间$T_i$。每个点有一些商品,商品共有k种。

对于第i个点的第j种商品,有买入花费$B_{i,j}$和卖出花费$S_{i,j}$,若为-1则代表该点不支持买入/卖出该商品。

你是一个商人,现在你希望找到一条盈利效率最高的环路。

环路是指从某个点出发,沿着边前进,经过若干个点并回到出发点,可以经过重复的点、重复的边。

在经过一个点时你可以选择买入或卖出物品,可以透支钱财、重复操作,但在任何时刻你最多只能同时携带一个物品。

一条环路的盈利效率等于$frac{买卖总收益}{花费总时间}$。

求最大的盈利效率,答案向下取整保留到整数。

$nleq 100,mleq 9900,kleq 1000,0leq S_{i,j},B_{i,j}leq 10^{9},1leq T_{i}leq 10^{7},图无重边无自环$。

题解:

看到求比值基本就是分数规划了。

一开始我想的是每个点设状态然后扔到一起跑SPFA式dp,也就是模拟行走的过程。

但这样的状态数有$10^{5}$个,而图本身又几乎是完全图,显然没希望。

注意到这个背包容量只有1的限制,不难发现整个过程可以抽象为$u_1 买 ightarrow u_2 卖 ightarrow u_3买 ightarrow u_4卖cdots$。

那么我们就可以只模拟买卖的过程,而中间的点走的肯定是最短路,预处理出来就行了。

于是先$Floyd$跑出任意两点$i,j$间的最短路$Dis_{i,j}$,然后二分答案x(值域为整数即可)。

对于两点$i,j$,边$i ightarrow j$的权值表示i买j卖的最大收益-时间花费,也就等于$max{S_{j,k}-B_{i,k}}-xDis_{i,j}$。

然后$SPFA$判非负环即可,注意在完全图上bfs跑得比dfs快。

复杂度$O(n^{2}klog{S_{max}})$。

套路:

  • 对局部过程建模不满足要求$ ightarrow$对整体过程建模。
  • 图上分数规划$ ightarrow$大部分模型都是判正负环。
  • 有大量具有相同性质的元素$ ightarrow$合并计算以降低复杂度。(同[SDOI2019]染色)

代码:

#include<bits/stdc++.h>
#define maxn 105
#define maxm 10005
#define maxk 1005
#define inf 0x7fffffff
#define ll long long
#define rint register ll
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
ll n,m,K,B[maxn][maxm],S[maxn][maxm],cnt;
ll hd[maxn],to[maxm],cst[maxm],nxt[maxm];
ll G[maxn][maxn],dis[maxn],len[maxn];
ll vis[maxn],inq[maxn]; queue<ll> q;

inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline void addedge(ll u,ll v,ll w){to[++cnt]=v,cst[cnt]=w,nxt[cnt]=hd[u],hd[u]=cnt;}
inline void Floyd(){
    for(ll k=1;k<=n;k++)
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++)
                G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
}

inline bool spfa(){
    while(!q.empty()) q.pop();
    memset(inq,0,sizeof(inq));
    memset(dis,-63,sizeof(dis));
    q.push(1),inq[1]=1,dis[1]=0,len[1]=0;
    while(!q.empty()){
        ll u=q.front(); q.pop(),inq[u]=0;
        for(ll i=hd[u];i;i=nxt[i]){
            ll v=to[i],w=cst[i];
            if(dis[v]<=dis[u]+w){
                dis[v]=dis[u]+w,len[v]=len[u]+1;
                if(len[v]>=n) return 1;
                if(!inq[v]) q.push(v),inq[v]=1;
            }
        }
    }
    return 0;
}

inline bool check(ll x){
    memset(hd,0,sizeof(hd)),cnt=0;
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=n;j++){
            ll w=0;
            for(ll k=1;k<=K;k++)
                if(B[i][k]!=-1 && S[j][k]!=-1)
                    w=max(w,S[j][k]-B[i][k]);
            addedge(i,j,w-x*G[i][j]);
        }
    return spfa();
}

int main(){
    n=read(),m=read(),K=read();
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=K;j++)
            B[i][j]=read(),S[i][j]=read();
    for(ll i=1;i<=n;i++) 
        for(ll j=1;j<=n;j++) G[i][j]=2e9;
    for(ll i=1;i<=m;i++)
        {ll u=read(),v=read(),w=read();G[u][v]=w;}
    Floyd();
    ll l=1,r=1e9,ans=0;
    while(l<=r){
        ll mid=l+r>>1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1; 
    }
    printf("%lld
",ans);
    return 0;
}
商旅
原文地址:https://www.cnblogs.com/YSFAC/p/13219511.html