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;
}