[BZOJ4898][APIO2017]商旅
题意
一张有向图,有\(k\)种商品,每种商品在每个点有一个买入价格和卖出价格,也有可能某个点不支持某种商品的买入或卖出。
同一时刻只能携带一种商品。
求一个回路使得收益最大。
答案向下取整。
sol
分数规划。
\(Floyed\)求出两两点对之间的距离\(d_{i,j}\)
\(O(n^2k)\)的枚举求出每两个点之间进行交易的最大收益\(w_{i,j}\)
然后就可以分数规划,令每条边的边权为\(k*d_{i,j}-w_{i,j}\),然后判断是否存在负环即可。
注意答案向下取整而不是四舍五入到整数。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 105;
const int K = 1005;
const double eps = 1e-2;
int n,m,k,b[N][K],s[N][K],w[N][N],d[N][N],vis[N];
double dis[N];
bool dfs(int u,double mid){
vis[u]=1;
for (int i=1;i<=n;++i)
if (d[u][i]<d[0][0]&&u!=i)
if (dis[i]>dis[u]+mid*d[u][i]-w[u][i]){
dis[i]=dis[u]+mid*d[u][i]-w[u][i];
if (vis[i]||dfs(i,mid)) return true;
}
vis[u]=0;return false;
}
bool check(double mid){
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;++i) if (dfs(i,mid)) return true;
return false;
}
int main(){
n=gi();m=gi();k=gi();
for (int i=1;i<=n;++i)
for (int j=1;j<=k;++j)
b[i][j]=gi(),s[i][j]=gi();
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
for (int l=1;l<=k;++l)
if (b[i][l]!=-1&&s[j][l]!=-1)
w[i][j]=max(w[i][j],s[j][l]-b[i][l]);
memset(d,63,sizeof(d));
for (int i=1;i<=n;++i) d[i][i]=0;
for (int i=1,u,v;i<=m;++i)
u=gi(),v=gi(),d[u][v]=gi();
for (int l=1;l<=n;++l)
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
d[i][j]=min(d[i][j],d[i][l]+d[l][j]);
double l=0,r=1e9;
while (r-l>eps){
double mid=(l+r)/2;
if (check(mid)) l=mid;else r=mid;
}
printf("%d\n",(int)l);return 0;
}