首先要明确为什么是用动态规划解这道题?
在分割多边形的题目中动规是一种比较常见的做法,本题的多边形经分割后的多边形显然还是一样的子问题,并且子问题的状态表示较为容易。
怎么写状态转移方程?
将输入的点人为地从1开始进行编号,dp[i][j] 表示点 i~j 形成的多边形的最佳分割结果。
这种dp的表示方式有一个需要解释的点:如果多边形的点不是连续的该怎么办?比如由点1、3、5、7构成的多边形。
其实不用管这种情况,因为在计算由点1、2、3、4、5构成的多边形的时候会计算到1、3、5这三个点,同理在计算其他多边形的时候也会包含1、3、5、7的其中一些情况,因此只要计算连续的点构成的多边形即可。并且最终的结果也是连续的点(dp[1][N])所以这种方法是可以保证是不会漏解的。
至于分割方式: 选择起点和终点构成的那条边,然后从剩余的点中选择一点构成三角形,这种分割方式可以保证分割后的两个多边形也是由连续的点构成的,正确性可以参考上面的解释。
此外还需要验证分割的合法性:分割后的多边形不能相交(即内部没有其他的点),只要ijk三点构成的三角形中没有其他的点就可以成立。验证方法参考:涉及三角形的运算
计算三角形的面积可以用向量法或海伦公式,在知道三点坐标的情况下推荐用向量法,相比海伦公式更加简单和高效,因为不用涉及开方运算。
AC代码:
#include <bits/stdc++.h>
#define mem(a) memset(a, 0, sizeof(a))
#define inf 0x3f3f3f3f
#define eps 1e-5
using namespace std;
struct point
{
double x;
double y;
} p[55];
int m;
inline double area(point a, point b, point c) //根据三点坐标计算三角形面积(向量法)
{
return fabs(a.x * c.y - a.x * b.y + b.x * a.y - b.x * c.y + c.x * b.y - c.x * a.y) / 2.0;
}
bool check(int a, int b, int c) //判断三角形abc内部是否包含其他点
{
double S = area(p[a], p[b], p[c]);
for (int i = 1; i <= m; i++)
{
if (i != a && i != b && i != c)
{
if (fabs(area(p[a], p[b], p[i]) + area(p[a], p[c], p[i]) + area(p[b], p[c], p[i]) - S) < eps)
{
return 0;
}
}
}
return 1;
}
int main()
{
int n;
double dp[51][51];
scanf("%d", &n);
while (n--)
{
scanf("%d", &m);
mem(dp);
for (int i = 1; i <= m; i++)
{
scanf("%lf %lf", &p[i].x, &p[i].y);
}
for (int i = 2; i < m; i++)
{
for (int j = 1; j <= m - i; j++)
{
dp[j][j + i] = inf;
for (int k = j + 1; k < j + i; k++)
{
if (check(j, j + i, k))
{
dp[j][j + i] = min(dp[j][j + i], max(area(p[j], p[j + i], p[k]), max(dp[j][k], dp[k][j + i])));
}
}
}
}
printf("%.1lf\n", dp[1][m]);
}
return 0;
}