[BZOJ4898][APIO2017]商旅

bzoj
luogu

题意

一张有向图,有\(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;
}
posted @ 2018-05-22 13:56  租酥雨  阅读(231)  评论(0编辑  收藏  举报