zoukankan      html  css  js  c++  java
  • [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;
    }
    商旅
  • 相关阅读:
    获取html页面传递过来的参数
    jqueryWeiui+pagehelper滚动加载(实现分页)
    【JS】js随笔
    【Java】Java基础
    【FrameWork】Hibernate
    【FrameWork】Struts2
    去掉inline-block间的间隙
    javascript单例模式
    关于call/apply与bind的一点误解
    git笔记-常用命令
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13219511.html
Copyright © 2011-2022 走看看