Convex
Time Limit: 10000/4000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1756 Accepted Submission(s): 523
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.
2 4 0 0 0 1 1 0 1 1 4 0 0 1 0 0 1 -1 -1
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,是j和i的夹角大于180度。
3:那么点i + 1到点j – 1的所有点都可以和点i构成不包括中心点的三角形。个数是
C(j – i – 1, 2)
具体算法如图:
注意到:如果每次找j点都从i点出发的话,那么算法就成了N3的复杂度了。其实每次的j点都是从上个j点之后开始的,降低一维复杂度。
另外,向这题顺次统计两个点的夹角时,由于会出现转了一圈的情况不好计算角度,所以在原来数组后面再顺次加上n-1一个点,角度同一加2pi,
for(int j = 0; j < n - 1; j++) ans[j + n - 1] = ans[j] + 2 * pi;
这样计算角度直接减就好了。
#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;
}