「APIO2017」商旅

4 篇文章 0 订阅
3 篇文章 0 订阅

link
不难证明,这个商人不会沿着同样的路径(e.g.1->2->3->1->2->3->1)走两次,因为答案都是一样的。

这样 33 p t s 33pts 33pts 的暴力就有了。考虑正解,有点像01分数规划?考虑二分最后答案,则
v a l [ x 1 ] [ x 2 ] + v a l [ x 2 ] [ x 3 ] + . . . + v a l [ x n ] [ x 1 ] t i m e [ x 1 ] [ x 2 ] + t i m e [ x 2 ] [ x 3 ] + . . . + t i m e [ x n ] [ x 1 ] > = m i d \frac {val[x1][x2]+val[x2][x3]+...+val[xn][x1]} {time[x1][x2]+time[x2][x3]+...+time[xn][x1]}>=mid time[x1][x2]+time[x2][x3]+...+time[xn][x1]val[x1][x2]+val[x2][x3]+...+val[xn][x1]>=mid
v a l val val t i m e time time 数组可以直接 F l o y d Floyd Floyd 预处理。令 g [ x 1 ] [ x 2 ] = v a l [ x 1 ] [ x 2 ] − m i d × t i m e [ x 1 ] [ x 2 ] g[x1][x2]=val[x1][x2]-mid\times time[x1][x2] g[x1][x2]=val[x1][x2]mid×time[x1][x2],则只用判断

g [ x 1 ] [ x 2 ] + g [ x 2 ] [ g 3 ] + . . . + g [ x n ] [ x 1 ] > = 0 g[x1][x2]+g[x2][g3]+...+g[xn][x1] >= 0 g[x1][x2]+g[x2][g3]+...+g[xn][x1]>=0,这个东西直接判正环(类似 f l o y d floyd floyd ,不会详见代码)。你会想,万一出现环套环怎么办,不妨分类讨论。若简单环的值本来就大于等于 0 0 0,直接 r e t u r n   1 return\ 1 return 1。就剩下简单环的值小于 0 0 0 的情况,那它们加起来也小于 0 0 0
难点:赋极大/极小值

Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define LL long long
#define Min(x, y) ((x)<(y)?(x):(y))
#define Max(x, y) ((x)>(y)?(x):(y))
using namespace std;
const int MAXN = 105, MAXM = 1e4 + 5, MAXK = 1e3 + 5;
int n, m, k, In[MAXN][MAXK], Out[MAXN][MAXK];
LL val[MAXN][MAXK], time_[MAXN][MAXK];
LL g[MAXN][MAXN];
void read(int &x) {
	x = 0; int f = 1; char c = getchar();
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = 0;
	for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
	x = f ? x : -x;
}
bool Check(LL x) { // 赋极大/极小值是难点 
	for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) {
		g[i][j] = time_[i][j] == 1e18 ? -1e18 : val[i][j] - time_[i][j] * x;
		if(g[i][j] <= -1e18) g[i][j] = -1e18;
	}
	for(int u = 1; u <= n; u ++) for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) if(g[i][j] <= 1e14) g[i][j] = max(g[i][j], g[i][u] + g[u][j]);
	for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) if(g[i][j] + g[j][i] >= 0) return 1;
	return 0;
}
int main() {
	int x, y;
	read(n); read(m); read(k);
	for(int i = 1; i <= n; i ++) {
		for(int j = 1; j <= k; j ++) {
			read(In[i][j]); read(Out[i][j]);
			if(In[i][j] == -1) In[i][j] = 1e9 + 1;
		}
	}
	for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) time_[i][j] = 1e18;
	for(int i = 1; i <= m; i ++) { read(x); read(y); scanf("%lld", &time_[x][y]); }
	for(int u = 1; u <= n; u ++) for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) time_[i][j] = Min(time_[i][j], time_[i][u] + time_[u][j]);
	for(int i = 1; i <= n; i ++) for(int j = 1; j <= n; j ++) if(time_[i][j] != 1e18) { val[i][j] = 0; for(int u = 1; u <= k; u ++) val[i][j] = Max(val[i][j], Out[j][u] - In[i][u]); }
	LL l = 0, r = 1e9, mid, res = 0;
	while(l <= r) {
		mid = (l + r) >> 1;
		if(Check(mid)) l = mid + 1, res = mid;
		else r = mid - 1;
	}
	printf("%lld", res);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值