UVALive 3132 Minimax Triangulation

题目大意:给你n个点围城的多边形,顺时针或者逆时针给你,起始点任意,让你把他划成n-2个三角形,这些划法中最大的三角形的面积最小,输出这个最小值。

思路:按照区间长度进行DP。对于 i~j 这些点,考虑新加入的点是j,那么就多了两条弦 i~j,j-1 ~j,对于 i~j 这条弦,加进去,它能围成的是 tran(i,k,j),i<k<j,,此时最大的三角形是 max( tran( i , k , j ) ,d[ i ][ k ] ,d[ k ][ j ] ),d[ i ][ j ] 表示按照题目给的方向来的 i ~ j 的最小的面积,对于这些又取最小,就是d[ i ][ j ] = min(max( tran( i , k , j ) ,d[ i ][ k ] ,d[ k ][ j ] ),d[ i ][ j ]),i<k<j,而同时,j - 1~j它围成的已经在d[ k ][ j ] 之中包含了, 综合起来就是上面那个式子,长度从4开始DP,初始化为长度3的,为了统一,要把长度1、2的变成0,。这里还有一个关键的地方,那就是 tran( i , k , j) 的时候,它有可能是凹多边形,要判断有没有出界,这个时候只要枚举除这三个点的全部的点,用面积和看看他们中间有没有点就行了。

自己想的时候,状态方程的有一步转移不会写,我 i~j 的写好了,然后是 j - 1~j 的了,可i 和j 并不是相邻的,没法用之前的 d 转移,看了别人的之后才发现原来不用了。。。 还有判断是否出界这个问题,我想的竟然是先判断是顺时针或逆时针,然后直接根据这个方向判断是否出界,一直WA,脑子抽了,用方向是能判断,也要跑一下所有点啊,三个点,又要判断方向,这样挺麻烦的,后来又看了下别人的,发现他们都是直接用面积和来判断的,这样比较方便的。。 = =

代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const double INF = 1e11;

const int MAXN = 55;

int n;

struct Point
{
    int x,y;
    void read()
    {
        scanf("%d%d",&x,&y);
    }
} p[MAXN];

inline double Cross(Point &a, Point &b, Point &c) {                    // 叉积
    return (b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y);
}

int check(int a,int b,int c)
{
    int tt = fabs(Cross(p[a],p[b],p[c]));
    for(int i = 0;i<n;i++)
    {
        if(i == a || i == b ||i == c) continue;
        int tmp = fabs(Cross(p[a],p[b],p[i])) + fabs(Cross(p[a],p[c],p[i])) + fabs(Cross(p[b],p[c],p[i]));
        if(tmp == tt) return 0;
    }
    return 1;
}

double d[MAXN][MAXN];

int main()
{
    int _;
    scanf("%d",&_);
    while(_--)
    {
        scanf("%d",&n);
        for(int i = 0;i<n;i++)
        {
            p[i].read();
        }
        for(int len = 1;len<=2;len++)
            for(int i = 0;i<n;i++)
                d[i][(i+len-1)%n] = 0;
        for(int i = 0;i<n;i++)
        {
            d[i][(i+2)%n] = fabs(Cross(p[i],p[(i+1)%n],p[(i+2)%n]))/2.0;
        }
        for(int len = 4;len<=n;len++)
        {
            for(int i = 0;i<n;i++)
            {
                int j = (i + len - 1)%n;
                d[i][j] = INF;
                for(int k = (i+1)%n; k!= j; k = (k+1)%n)
                {
                    if(check(i,k,j)) d[i][j] = min(d[i][j],max(max(d[i][k],d[k][j]),fabs(Cross(p[i],p[k],p[j]))/2.0));
                }
                //printf("i = %d,j = %d,d = %f\n",i,j,d[i][j]);
            }
        }
        double ans = INF;
        for(int i = 0;i<n;i++)
            ans = min(d[i][(i+n-1)%n],ans);
        printf("%.1f\n",ans);
    }
    return 0;
}

/*

6
1 1
0 3
3 5
9 5
6 2
7 0

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值