APIO2017 商旅
APIO2017 商旅
题意
给出一个\(N\)个点\(M\)条边的有向图,你可以在每个点对于一些特定的商品进行买卖。一共有\(K\)种商品,在每一个点,每一种商品都有一个买入值\(B_i\)和一个卖出值\(S_i\)。每一条边有一个边权\(T_i\),表示经过这条边的时间。定义一条环路的盈利效率为在这条环路上能够获得的利益最大值除以在这条环路上花费的时间,求盈利效率最大的一条环路。
\((1 \leq N \leq 100 , 1 \leq M \leq 9900 , 1 \leq K \leq 1000 , 1 \leq B_i , S_i \leq 10^9 , 1 \leq T_i \leq 10^7)\)
题解
首先我们可以得出盈利效率的计算方法是\(ans=\frac{\sum Val}{\sum T}\),然后我们会发现这个题目就会和BZOJ的最小圈那道题目有点类似了。继续考虑到这道题目中\(N\)的范围比较的小,所以我们可以考虑稍微暴力一些的方法。实际上,对于仍以两个点\(u\)、\(v\),如果我们强制让这两个点分别为某一次交易的起点和终点,那么这一次交易的利益就是确定了的,所以我们可以之间从\(u\)向\(v\)连一条\(val\)为这次交易的利益(如果是负的就不用连了),\(T\)为这两点之间的最短路的边,然后我们就可以转化成最小圈那题做了。
Code:
#include<bits/stdc++.h>
using namespace std;
const int M=10005;
const int N=105;
typedef long long ll;
#define int ll
typedef pair<ll,int>P;
#define fi first
#define se second
#define mk make_pair
int n,m,nk,tot;
ll B[N][N*30],S[N][N*30];
ll dis[N][N],Mp[N][N];
struct edge {
int to,nxt;
ll w;
}E[M];
int head[N];
void Addedge(int u,int v,ll w) {
E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot;E[tot].w=w;
}
queue<int>Q;
ll di[N],inq[N],cnt[N],vis[N];
int Spfa() {
while(!Q.empty()) Q.pop();
memset(cnt,0,sizeof cnt);memset(di,0,sizeof di);
for(int i=1;i<=n;i++) Q.push(i),inq[i]=1;
while(!Q.empty()) {
int o=Q.front();Q.pop();
cnt[o]++;
if(cnt[o]>n) return 1;
inq[o]=0;
for(int i=head[o];~i;i=E[i].nxt) {
int to=E[i].to;
if(di[to]<=di[o]+E[i].w) {
di[to]=di[o]+E[i].w;
if(!inq[to]) {
inq[to]=1;
Q.push(to);
}
}
}
}
return 0;
}
int Check(ll ret) {
memset(head,-1,sizeof head);
tot=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(i==j||Mp[i][j]<0) continue;
Addedge(i,j,Mp[i][j]-ret*dis[i][j]);
}
}
return Spfa();
}
signed main() {
scanf("%lld%lld%lld",&n,&m,&nk);
for(int i=1;i<=n;i++) {
for(int j=1;j<=nk;j++) {
scanf("%lld%lld",&B[i][j],&S[i][j]);
}
}
memset(dis,0x3f,sizeof dis);
for(int i=1,u,v;i<=m;i++) {
ll t;
scanf("%lld%lld%lld",&u,&v,&t);
dis[u][v]=min(dis[u][v],t);
}
for(int k=1;k<=n;k++) {
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(dis[i][j]>1e10||i==j) continue;
Mp[i][j]=0;
for(int k=1;k<=nk;k++) {
if(B[i][k]==-1||S[j][k]==-1) continue;
Mp[i][j]=max(Mp[i][j],S[j][k]-B[i][k]);
}
}
}
ll L=0,R=1e9,ans=0;
while(L<=R) {
ll Mid=(L+R)>>1;
if(Check(Mid)) L=Mid+1,ans=Mid;
else R=Mid-1;
}
printf("%lld\n",ans);
return 0;
}
「我不敢下苦功琢磨自己,怕终于知道自己并非珠玉;然而心中既存着一丝希冀,便又不肯甘心与瓦砾为伍。」