给定一个 n
n
条边的多边形(不一定是凸多边形),用 n−3 条线段(线段必须连接多边形上的两点,每条线段都必须在多边形的内部,并且任意两条线段都不能在多边形内相交)把多边形剖分成 n−2
n
−
2
个三角形,试找出一个切割方案,使得最大的三角形面积最小。
Input
第一行一个整数 T
T
表示数据组数。
每组数据的第一行一个整数 n。
接下来 n
n
行,按顺时针或逆时针顺序给出 n 边形的一个点 (x,y)
(
x
,
y
)
。
Output
一个实数,表示最大面积,保留1位小数。
Sample Input
6 7 0 6 2 9 5 3 5 0 3 1 1
Sample Output
9.0
Solution
先把点统一处理成逆时针顺序(若用叉积算多边形面积为负数则为顺时针)。
设 f[l][r]
f
[
l
]
[
r
]
表示从多边形上第 l
l
个点 Al 到第 r
r
个点 Ar 之间的区域全部分割成三角形后,最大三角形面积的最小值。
注意因为多边形首尾顺次相接,l
l
可以大于 r。
记 nxt[i]=i+1(1≤i<n),nxt[n]=1
n
x
t
[
i
]
=
i
+
1
(
1
≤
i
<
n
)
,
n
x
t
[
n
]
=
1
,则 DP 边界为 f[i][nxt[i]]=0
f
[
i
]
[
n
x
t
[
i
]
]
=
0
。
转移为:f[l][r]=min{max{f[l][i],S△AlAiAr,f[i][r]}}(AiAr→×AiAr→>0)
f
[
l
]
[
r
]
=
min
{
max
{
f
[
l
]
[
i
]
,
S
△
A
l
A
i
A
r
,
f
[
i
]
[
r
]
}
}
(
A
i
A
r
→
×
A
i
A
r
→
>
0
)
注意题目给出的多边形不一定是凸多边形,可能存在如下情况:
此时 △AlAiAr
△
A
l
A
i
A
r
是不合法的,但同时我们也会发现用叉积计算结果为负数,因此 DP 就只要判断 AiAr→×AiAr→>0
A
i
A
r
→
×
A
i
A
r
→
>
0
。
最后答案为 min{max{f[i][j],f[j][i]}}(i≠j)
min
{
max
{
f
[
i
]
[
j
]
,
f
[
j
]
[
i
]
}
}
(
i
≠
j
)
。
转移顺序不好处理,可用记忆化搜索实现。
Code
#include <iostream>#include <cstdio>#include <algorithm>usingnamespacestd;
constint Maxn = 0x3f3f3f3f;
constint N = 55;
int nxt[N], f[N][N], n, m, Ans;
struct point
{
int x, y;
point() {}
point(int X, int Y):
x(X), y(Y) {}
friendinline point operator - (const point &a, const point &b)
{
return point(b.x - a.x, b.y - a.y);
}
friendinlineintoperator * (const point &a, const point &b)
{
return a.x * b.y - b.x * a.y;
}
}a[N];
inlineint Max(int x, int y) {return x > y ? x : y;}
inlineint Min(int x, int y) {return x < y ? x : y;}
inlinevoid CkMin(int &x, int y) {if (x > y) x = y;}
inlineint dp(int l, int r)
{
if (f[l][r] != -1) return f[l][r];
if (r == nxt[l]) return f[l][r] = 0;
int res = Maxn;
for (int i = nxt[l]; i != r; i = nxt[i])
{
int tmp = (a[i] - a[r]) * (a[i] - a[l]);
if (tmp > 0) CkMin(res, Max(Max(dp(l, i), tmp), dp(i, r)));
}
return f[l][r] = res;
}
int main()
{
while (scanf("%d", &m) != EOF)
{
while (m--)
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d", &a[i].x, &a[i].y);
for (int i = 1; i < n; ++i)
nxt[i] = i + 1; nxt[n] = 1;
int sum = 0;
for (int i = 1; i < n; ++i)
sum += a[i] * a[i + 1]; sum += a[n] * a[1];
if (sum < 0)
for (int i = 1, im = n >> 1; i <= im; ++i)
swap(a[i], a[n - i + 1]);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
f[i][j] = -1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (i != j) dp(i, j);
Ans = Maxn;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (i != j) CkMin(Ans, Max(f[i][j], f[j][i]));
printf("%.1lf\n", (double)Ans / 2.0);
}
}
return0;
}