2020 Petrozavodsk Winter Camp Day 5 H

Problem H. Lighthouses

Statement

给一个有 $n$ 个顶点的凸多边形,其顶点用 $(x_i,y_i)$ 表示。以 $n$ 个顶点为结点,给定 $m$ 条边 $(u_i,v_i)$。希望求出图上最长的(指欧几里得距离)且不和自己相交的(几何意义上)路的长度。有 $z$ 组测试数据。

$3 \leq n \leq 300$, $0 \leq m \leq \frac{n(n-1)}{2}$, $1 \leq u_i \neq v_i \leq n$

$-10^9 \leq x_i, y_i \leq 10^9$

$\sum n \leq 3000$

Solution

这题赛中我读都没读就被 clp012345 切了,赛后才看。

经过观察我们发现其实合法的路只能是在环上往一个方向转,那么我们上一个区间 DP 就可以了。开三个维度,分别表示起点、终点、顺/逆时针,状态转移 $O(n)$ 枚举。时间复杂度 $O(n^3)$。

区间 DP 要上环的话,通常做法就是开两倍数组复制一遍吧,参考石子合并。

Code (By clp012345)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <bits/stdc++.h>

#define FOE(i, s, t) for (int i = s; i <= t; i++)
#define FOR(i, s, t) for (int i = s; i < t; i++)
#define K 601
#define M 1000000007
#define LL long long

using namespace std;

int n;
int x[K], y[K];

double dist(int id1, int id2) {
double dx = abs(x[id1] - x[id2]);
double dy = abs(y[id1] - y[id2]);

return sqrt(dx * dx + dy * dy);
}

int ok[K][K][2];
double dp[K][K][2];

int has[K][K];

int prev(int u) {
return (u == 1 ? n : u - 1);
}

int nxt(int u) {
return (u == n ? 1 : u + 1);
}

double solve2(int sta, int fin, int dir) {
if (sta == fin) return 0.00;
if (ok[sta][fin][dir] == 0) {
dp[sta][fin][dir] = 0.00;
ok[sta][fin][dir] = 1;
if (dir == 1) {
// clockwise
for (int i = nxt(sta); i != fin; i = nxt(i)) {
// printf("sta %d fin %d i %d %d\n", sta, fin, i, has[sta][i]);
if (has[sta][i]) {
dp[sta][fin][dir] = max(dp[sta][fin][dir], max(solve2(i, fin, 1), solve2(i, sta, 0)) + dist(sta, i));
}
}

} else {
// anti clockwise
for (int i = prev(sta); i != fin; i = prev(i)) {
if (has[sta][i]) {
dp[sta][fin][dir] = max(dp[sta][fin][dir], max(solve2(i, fin, 0), solve2(i, sta, 1)) + dist(sta, i));
}
}
}
}

return dp[sta][fin][dir];
}

void solve() {
scanf("%d", &n);

FOE(i, 1, n) scanf("%d%d", &x[i], &y[i]);

FOE(i, 1, n) FOE(j, 1, n) ok[i][j][0] = ok[i][j][1] = 0;

double ret = 0.00;

FOE(i, 1, n) FOE(j, 1, n) has[i][j] = 0;
int m; scanf("%d", &m);

FOE(i, 1, m) {
int u, v; scanf("%d%d", &u, &v);
has[u][v] = has[v][u] = 1;
}

FOE(i, 1, n) FOE(j, 1, n) if (has[i][j]) {
double temp = max(solve2(j, i, 1), solve2(j, i, 0)) + dist(i, j);

if (temp > ret) ret = temp;
}

printf("%.9f\n", ret);
}

int main() {
int t; scanf("%d", &t);
while (t--) solve();
}