luogu P3778 [APIO2017]商旅

时间:2021-04-17
本文章向大家介绍luogu P3778 [APIO2017]商旅,主要包括luogu P3778 [APIO2017]商旅使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题面传送门
一看到效率就感觉像是分数规划。
然后这个东西又可以二分。
所以应该是二分寻找答案。
那么怎么看\(mid\)是否可以呢?
看到\(n\)很小想到可以\(O(n^3)\)
首先floyd预处理出两点之间距离。
然后再\(O(n^2k)\)暴力枚举得到两点之间最优的值。
那么二分一个答案之后把每条边边权设为\(w_{i,j}-mid\times dis_{i,j}\)即可,然后跑floyd看看有没有正环。
时间复杂度\(O(n^3logw+n^2k)\)
code:

#include<cstdio>
#include<cstring>
#define N 139
#define K 1039
#define I inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,a[N][K],b[N][K],w[N][N],x,y,z;ll f[N][N],g[N][N],l,r,mid;
I int check(ll mid){
	register int i,j,k;memset(g,-0x1f,sizeof(g));
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++)i^j&&(f[i][j]<=1e9)&&(g[i][j]=w[i][j]-f[i][j]*mid);
	}
	for(k=1;k<=n;k++){
		for(i=1;i<=n;i++){
			if(i==k)continue;
			for(j=1;j<=n;j++) (j^k)&&(g[i][j]=max(g[i][j],g[i][k]+g[k][j]));
		}
	}
	for(i=1;i<=n;i++) if(g[i][i]>=0) return 1;return 0;
}
int main(){
	freopen("1.in","r",stdin);
	register int i,j,h;scanf("%d%d%d",&n,&m,&k);memset(f,0x3f,sizeof(f));
	for(i=1;i<=n;i++){
		for(j=1;j<=k;j++) scanf("%d%d",&a[i][j],&b[i][j]),(a[i][j]==-1)&&(a[i][j]=1e9),(b[i][j]==-1)&&(b[i][j]=0);
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			if(i==j)continue;
			for(h=1;h<=k;h++)w[i][j]=max(w[i][j],b[j][h]-a[i][h]);
		}
	}
	for(i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),f[x][y]=z;
	for(i=1;i<=n;i++) f[i][i]=0;
	for(h=1;h<=n;h++){
		for(i=1;i<=n;i++){
			if(h==i) continue;
			for(j=1;j<=n;j++) (i^j)&&(j^h)&&(f[i][j]=min(f[i][h]+f[h][j],f[i][j]));
		}
	}
	l=0;r=1e9;while(l+1<r)mid=l+r>>1,check(mid)?(l=mid):(r=mid);printf("%lld\n",l);
}

原文地址:https://www.cnblogs.com/275307894a/p/14670698.html