题意:
有一张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; }