【HDU3629】n个点组成的凸四边形数 极角atan2+组合数

Convex

Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1756    Accepted Submission(s): 523


Problem Description
Your task is very simple. You will get the coordinates of n points in a plane. It is guaranteed that there are no three points on a straight line. You can choose any four points from these points to construct a quadrangle. Now, please tell me how many convex quadrangles you can construct.
 

Input
The first line of input contain an integer z (z ≤ 20), indicating the number of test cases.
For each test case, the first line contain an integer n (4 ≤ n ≤ 700), indicating the number of points. Each of the next n lines contains two integers x and y (-1000000 ≤ x, y ≤ 1000000), indicating the coordinate of corresponding point.

 

Output
For each test case, just output a single integer, the number of convex quadrangles you can construct.
 

Sample Input
  
  
2 4 0 0 0 1 1 0 1 1 4 0 0 1 0 0 1 -1 -1
 

Sample Output
  
  
1 0
 

题目描述:给你n个点(4~700), 问你能够成多少个不同的凸四边形。

解题报告:

暴力的话C(700,4)必然超时,发现,任何一个凹包必然是其中一点在其它3点构成的三角形内。

然后就考虑,能不能求出所有凹包的个数,然后用总数C(n, 4)减去凹包的个数,就是答案。

依次枚举每个点i,看看其它点能够成多少个包括点i的三角形,就是以这个点为中心的凹包的个数。

找三角形也要一定的技巧,也是通过逆向思维:找出有多少个三角形不包括点i,然后用总三角形个数C(n – 1, 3) – 这个个数就是就是以这个点为中心的凹包的个数。

注意到,如果3个点不能圈住中心点,则必然是存在一条通过中心点的直线,使得这三个点都在直线的同侧。利用这个形式,我们用如下方法寻找三角形:

1:以中心点为中心,对剩余n-1个点极角排序。

2:依次处理排序后的n-1个点,对于每一个点i,依次往后扫描,找到第一个点j,是ji的夹角大于180度。

3:那么点i + 1到点j – 1的所有点都可以和点i构成不包括中心点的三角形。个数是

C(j – i – 1, 2)

具体算法如图:

 

HDU <wbr>3629 <wbr>计算几何 <wbr>2010天津网络预赛 <wbr>计算技巧

注意到:如果每次找j点都从i点出发的话,那么算法就成了N3的复杂度了。其实每次的j点都是从上个j点之后开始的,降低一维复杂度。

 

         自己以前没有注意到的代码技巧:

         计算极角时,如果是负数(-pi ~ 0),就把它加上2 * pi,这样就把角度统一到了0~2pi。

另外,向这题顺次统计两个点的夹角时,由于会出现转了一圈的情况不好计算角度,所以在原来数组后面再顺次加上n-1一个点,角度同一加2pi,

for(int j = 0; j < n - 1; j++) ans[j + n - 1] = ans[j] + 2 * pi;

这样计算角度直接减就好了。




Source

#define DeBUG
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <string>
#include <set>
#include <sstream>
#include <map>
#include <list>
#include <bitset>
using namespace std ;
#define zero {0}
#define INF 0x3f3f3f3f
#define EPS 1e-6
#define TRUE true
#define FALSE false
typedef long long LL;
const double PI = acos(-1.0);
//#pragma comment(linker, "/STACK:102400000,102400000")
inline int sgn(double x)
{
    return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);
}
#define N 705

template<class T> T sqr(T x)//求平方
{
    return x * x;
}
// Point class
struct Point;
typedef Point Vec;
struct Point
{
    double x, y;
    Point () {}
    Point(double a, double b)
    {
        x = a;
        y = b;
    }

};

Vec operator + (const Vec &a, const Vec &b) //点加法
{
    return Vec(a.x + b.x, a.y + b.y);
}
Vec operator - (const Vec &a, const Vec &b) //点减法
{
    return Vec(a.x - b.x, a.y - b.y);
}
Vec operator * (const Vec &a, const double &p) //点与常数相乘
{
    return Vec(a.x * p, a.y * p);
}
Vec operator / (const Vec &a, const double &p) //点除以常数
{
    return Vec(a.x / p, a.y / p);
}
bool operator < (const Vec &a, const Point &b) //平面直角坐标系中左下方的为小
{
    return a.x < b.x || (sgn(a.x - b.x) == 0 && a.y < b.y);
}
bool operator == (const Vec &a, const Point &b) //点相等判断
{
    return sgn(a.x - b.x) == 0 && sgn(a.y - b.y) == 0;
}

//===========================================================================

inline double ptDis(Point a, Point b)//点间距
{
    return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}
inline double dotDet(Vec a, Vec b)//点乘
{
    return a.x * b.x + a.y * b.y;
}
inline double crossDet(Vec a, Vec b)//叉乘
{
    return a.x * b.y - a.y * b.x;
}
inline double crossDet(Point o, Point a, Point b)//向量叉乘
{
    return crossDet(a - o, b - o);
}
inline double vecLen(Vec a)//求一点到原点距离
{
    return sqrt(dotDet(a, a));
}
double ans[2000];
Point p[N];
long long C(int a, int b)
{
    long long aa = a, bb = b, ans = 1;
    for (int i = 0; i < b; i++)ans *= (aa - i);
    for (int i = 2; i <= b; i++)ans /= i;
    return ans;
}
int main()
{
#ifdef DeBUGs
    freopen("C:\\Users\\Sky\\Desktop\\1.in", "r", stdin);
#endif
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n;
        scanf("%d", &n);
        for (int i = 0; i < n; i++)
        {
            scanf("%lf%lf", &p[i].x, &p[i].y);
        }
        long long re=0;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                if (j == i)
                    continue;
                double tmp = atan2(p[j].y - p[i].y, p[j].x - p[i].x);
                if (sgn(tmp) <= 0)
                    tmp += 2 * PI;
                j < i ? ans[j] = tmp : ans[j - 1] = tmp;
            }
            sort(ans, ans + n - 1);
            int k = 1;
            long long re2 = 0;
            for (int j = 0; j < n - 1; j++)
                ans[j + n - 1] = ans[j] + 2 * PI;
            for (int j = 0; j < n - 1; j++)
            {
                while (fabs(ans[k] - ans[j]) - PI < 0)
                {
                    k++;
                }
                if (k - j - 1 >= 2)
                    re2 += C(k - j - 1, 2);
            }
            re += C(n - 1, 3) - re2;
        }
        printf("%I64d\n", C(n, 4) - re);
    }

    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值