当前位置: 首页 > 工具软件 > Tc-play > 使用案例 >

TC SRM566 DIV2 T3 FencingPenguinsEasy

翟俊哲
2023-12-01

前言

头一道计算几何题,现学现用…

题目描述

En

Paco collects penguins. His penguins like to play and run around on the ice where he lives. In order to keep his penguins safe he decided to construct a single fence enclosure to keep danger out.

Paco’s penguins have fallen asleep. Acting quickly Paco placed numPosts posts in a circular configuration on the ice. Each post is placed radius meters from location (0,0) ( 0 , 0 ) . The posts are equally spaced with the first post being placed at location (radius,0) ( r a d i u s , 0 ) .

To construct the fencing, Paco will connect some pairs of fence posts using straight fences. No two segments of the fence are allowed to cross. In the resulting fencing, each fence post will either be unused, or it will be connected to exactly two other fence posts. In order to minimize the damage his penguins cause to the ice, he would like to minimize the area enclosed by the fence.

In order to keep all his penguins happy Paco would like to have all his penguins in the one single enclosure. Two penguins are in the same enclosure if it is possible to walk from one penguin to the other without crossing a fence.

You are given two ints numPosts and radius. You are also given two vector s x and y representing the (x,y) ( x , y ) location of each of the sleeping penguins. Each penguin is small enough to be considered a point. Compute an return the smallest area of an enclosure that can contain all the penguins. If it is not possible to enclose all the penguins return 1 − 1 .

Ch

大意就是有一圈栏杆,等距分布在一个圆上,有一些坐标给定的点,要你将若干栏杆之间用线连成一个封闭图形,且这些点全部包括在内。求围成封闭图形最小的面积。

数据范围

  • numPosts n u m P o s t s will be between 3 3 and 222, inclusive.
  • radius will be between 5 5 and 100,000, inclusive.
  • x x will contain between 1 and 50 50 elements, inclusive.
  • y y will contain the same number of elements as x.
  • Each element in x x will be between 100,000 and 100,000 100 , 000 , inclusive.
  • Each element in y y will be between 100,000 and 100,000 100 , 000 , inclusive.
  • No penguin will come within 106 10 − 6 of any potential fencing.
  • No two penguins will occupy the same location.jihe

分析

首先因为刚刚学计算几何,我很自然想到了凸包这个东西。其实直接去考虑这个封闭图形(为了方便我们接下来就称为闭包)的形态是非常复杂的。我们可以像求凸包一样,比如定义一个上下的方向,先确定中间分界线的两个点,然后上下两半分开求个最小面积,再合并。当然事实上为了简化,我们可以只考虑一半,最后再把这两个点直接连起来。因此考虑DP,方程有点区间DP的样子:令 DP[i][j] D P [ i ] [ j ] 表示这两个点分别为 i,j i , j 时,上面的闭包可行的最小面积。则 DP[i][j]=DP[i][k]+S(i,j,k) D P [ i ] [ j ] = D P [ i ] [ k ] + S ( i , j , k ) ,其中 S(i,j,k) S ( i , j , k ) 表示以 i,j,k i , j , k 三个点为端点的三角形面积,且 ikj i ≤ k ≤ j 。于是剩下需要注意的就是判断一下状态是否可行了,时间复杂度 O(N4) O ( N 4 )

参考程序

const double Pie = acos(-1);
typedef vector<int> Arr;
struct Point;
typedef Point Vector;
struct Point {
    double x, y;
    Point() {}
    Point(double x_, double y_): x(x_), y(y_) {}
    Point rotate(double ang) { return Point(x * cos(ang) - y * sin(ang), x * sin(ang) + y * cos(ang)); }      // 坐标旋转
    Vector operator-(const Point & p) { return Vector(x - p.x, y - p.y); }
    void print() const { printf("(%.2f, %.2f)\n", x, y); }
};
double cross(const Vector & u, const Vector & v) {      // 叉积
    return u.x * v.y - u.y * v.x;
}
double cross(const Point & A, const Point & B, const Point & C) {
    return (B.x - A.x) * (C.y - A.y) - (B.y - A.y) * (C.x - A.x);
}

vector<Point> Pnt, Pgns;

class FencingPenguinsEasy {jwsd
public:
    double calculateMinArea( int numPosts, int radius, Arr x, Arr y );
private:
    int N;
    bool check_sugmnt(Point A, Point B) {   //  判断是否有点在该线段外(顺时针方向)
        Vector u = B - A;
        for (int i = 0; i < N; i++)
            if (cross(u, Pgns[i] - A) < 0) return false;jihe
        return true;
    }
};

const int ArSize = 230;
const double INF = 1e20;    // INF定大点
double DP[ArSize][ArSize];

double FencingPenguinsEasy::calculateMinArea(int numPosts, int radius, Arr x, Arr y) {
    int i;
    Pgns.clear(), Pnt.clear();
    for (i = 0, N = x.size(); i < N; ++i) Pgns.push_back(Point(x[i], y[i]));
    double ang = 2 * Pie / numPosts;
    for (i = 0; i < numPosts; i++) Pnt.push_back(Point(radius * cos(ang * i), radius * sin(ang * i)));     // 预处理出所有栏杆的点
    for (i = 1; i < numPosts; i++)
        if (!check_sugmnt(Pnt[i - 1], Pnt[i])) return -1;   // 先check会不会有点在最大的圈外面
    if (!check_sugmnt(Pnt[numPosts - 1], Pnt[0])) return -1;
    int j, k;
    for (i = 0; i < numPosts; i++)
        for (j = 0; j < numPosts; j++) DP[i][j] = i == j ? 0 : INF; // 初始化
    for (i = 2; i < numPosts; i++)
        for (j = i - 1; j >= 0; j--)    // 这里i,j反了一下
            for (k = j + 1; k <= i; k++)    // 枚举k的时候i,j两个端点只要枚举到一个就可以了,因为两个其实是相同的
                if (check_sugmnt(Pnt[j], Pnt[k]))   // 在k,i状态上拓展一个三角形i,j,k变成j,i,要判断j,k是否合法
                    DP[j][i] = min(DP[j][i], DP[k][i] + fabs(cross(Pnt[i], Pnt[j], Pnt[k])) / 2);
    double res = INF;
    for (i = 2; i < numPosts; i++)
        for (j = i - 2; j >= 0; j--)
            if (check_sugmnt(Pnt[i], Pnt[j])) res = min(res, DP[j][i]); // 判断i,j连起来是否合法
    return res;
}

总结

这题其实挺暴力的,当然计算几何基础要熟练,因为其实算法的核心思维的灵感也是来自计算几何的算法。

 类似资料:

相关阅读

相关文章

相关问答