View on GitHub

Wenchong Huang

旧博客,文章搬迁完后删除

返回首页 返回专题

【APIO2017】商旅 题解

发现点数很少,这就允许了我们随便乱搞

首先,对于任意点对(u,v),我们可以预处理出u点买入v点卖出的最大盈利,以及u到v的最短距离,这个过程可以用Floyd来完成

处理出了这个就非常好办了,建一张新的图,对于所有可以获得盈利的点对(u,v),我们从u向v连一条边,保存u到v的最大盈利以及最短距离

题目要求我们就是要最大化 Σ(盈利)/Σ(距离),我们使用分数规划

为了表述方便,下面记盈利为w,距离为d

考虑二分答案,假设有一个答案ans,它满足:

对柿子稍作变换,得到:

若左式取得最大值时不等式成立,则不等式恒成立,说明存在更优解,需要调整二分下界

那么我们可以调整图中的边权,将边权设为w-ans*d,然后判定正权环即可

同样的套路,边权取相反数变成判负环,然后用DFS-SPFA来做

#include<bits/stdc++.h>
#define double long double
#define INF 1e11
using namespace std;

const double eps=5*1e-8;
struct Edge{int to,next;double capa;} e[20010];
int h[110],sum=0,n,m,K;
double dis[110][110];
double buy[110][1010],sell[110][1010];
double cap[20010],val[20010],earn[110][110];
double d[110];bool vis[110],flag;

void add_edge(int u,int v)
{
	e[++sum].to=v;
	cap[sum]=dis[u][v];
	val[sum]=earn[u][v];
	e[sum].next=h[u];
	h[u]=sum;
}

void dfs(int u)
{
	if(flag) return;
	vis[u]=1;
	for(int tmp=h[u];tmp;tmp=e[tmp].next)
		if(d[u]+e[tmp].capa<d[e[tmp].to])
		{
			if(vis[e[tmp].to]||flag){flag=1;return;}
			d[e[tmp].to]=d[u]+e[tmp].capa;
			dfs(e[tmp].to);
		}
	vis[u]=0;
}

bool chk(double x)
{
	for(int i=1;i<=sum;i++)
		e[i].capa=x*cap[i]-val[i];
	memset(vis,0,sizeof(vis));
	memset(d,0,sizeof(d));
	flag=0;
	for(int i=1;i<=n;i++)
		if(!flag) dfs(i);
	return flag;
}

int main()
{
	int u,v;double w;
	scanf("%d%d%d",&n,&m,&K);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=K;j++)
			scanf("%Lf%Lf",&buy[i][j],&sell[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dis[i][j]=(i==j)?0:INF;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%Lf",&u,&v,&w);
		dis[u][v]=min(dis[u][v],w);
	}
	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(i!=j&&dis[i][j]<INF-eps)
			{
				for(int k=1;k<=K;k++)
					if(fabs(buy[i][k]+1)>eps&&fabs(sell[j][k]+1)>eps)
						earn[i][j]=max(earn[i][j],sell[j][k]-buy[i][k]);
				add_edge(i,j);
			}
	double l=0,r=1e9,mid;
	while(r-l>eps)
	{
		mid=(l+r)/2;
		if(chk(mid)) l=mid;
		else r=mid;
	}
	printf("%d\n",(int)floor(r));
	return 0;
}