Minimax Triangulation-uva1331 紫书P280计算几何动规

vjudge上的题目链接

首先要明确为什么是用动态规划解这道题?

在分割多边形的题目中动规是一种比较常见的做法,本题的多边形经分割后的多边形显然还是一样的子问题,并且子问题的状态表示较为容易。

怎么写状态转移方程?

将输入的点人为地从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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值