一:求凸包的简洁板子
1:求凸包
#include <iostream>
#include <algorithm>
#include <iomanip>
using namespace std;
const int maxn = 50010;
struct Point {
int x, y;
bool operator < (Point const& rhs) const {//重载为类的成员函数的时候,形参时运算符的右操作数
return (x < rhs.x) || (x == rhs.x && y < rhs.y);
}
};
int Cross(Point const& O, Point const& A, Point const& B)
{
int xoa = A.x - O.x;
int xob = B.x - O.x;
int yoa = A.y - O.y;
int yob = B.y - O.y;
return xoa * yob - xob * yoa;
}
double Dis(Point const& A, Point const& B)
{
double xoa = A.x - B.x;
double xob = A.y - B.y;
return sqrt(xoa * xoa + xob * xob);
}
int Andrew(Point p[], int n, Point ch[])
{
sort(p, p + n);
int m = 0;
for (int i = 0; i < n; i++) { //下凸包
while (m > 1 && Cross(ch[m - 2], ch[m - 1], p[i]) < 0) m--;
ch[m++] = p[i];
}
int k = m;
for (int i = n - 2; i >= 0; i--) { //上凸包
while (m > k && Cross(ch[m - 2], ch[m - 1], p[i]) < 0) m--;
ch[m++] = p[i];
}
if (n > 1) m--;
return m;
}
Point p[maxn], ch[maxn];
int main()
{
int n;
while (cin >> n)
{
for (int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
int m = Andrew(p, n, ch); //求凸包
………………
}
return 0;
}
随笔语录
//旋转卡壳的核心思想
1:首先过凸包的任何一个顶点做一条直线,但是我们要保证凸包位于直线的一侧。
2:过凸包的另外一个顶点做该直线的平行线,于是这一对点可以称为凸包的一对踵点(一个n边凸边形最多有3*n/2(向上取整)对蹱点;
3:再编程的时候我们为了方便实现,通常我们是将其中的一条直线旋转到与凸包的一条边重合,我们可以简单的想象成这两条直线卡住了凸包的一条边和一个顶点;
4:现在我们来考虑,如何计算凸包的直径呢?先求凸包上的点到直线的最大距离,凸包上的点到对应边的距离是呈现一个单峰函数的形式,点到直线的距离是呈现一个先上升到最大值后然后下降,点到直线的距离我们可以转换成三角线的面积来求;
5:枚举每一对踵点,更深刻的讲是枚举每一条边,这条边的两个端点分别用i和i+1来表示,对应平行线的的一个点我们用q来表示,用while循环,当三角形i,i+1,q+1的面积比i,i+1,q的面积大的时候,我们就更新q,让q自增1,记得要取余凸包顶点的个数,然后我再比较q这个点和对应直线两个端点之间的距离取大的值,随着一条直线的旋转,不断更新最大值,我们就可以将凸包的直径求出来。
随手笔记
//求凸包的核心思想
1:叉积的定义:当前向量和新的向量叉乘,如果结果大于0
求三角形的最大面积
//旋转卡壳法
int ans = 0;
for (int i = 0; i < m; i++) {
int q = 1;
for (int j = i + 1; j < m; j++) {
while (Cross(ch[i], ch[j], ch[q + 1]) > Cross(ch[i], ch[j], ch[q]))
q = (q + 1) % m;
ans = max(ans, Cross(ch[i], ch[j], ch[q]));
}
}
printf("%.2lf\n",ans/2);
求凸包的最大直径
//旋转卡壳法求最大半径
if (m == 1) {
printf("%.2ld\n", Dis(ch[0], ch[1]));
}
else {
double ans = 0;
ch[m] = ch[0];//把第一个点放到最后
int j = 2;
for (int i = 0; i < m; i++) {
while (Cross(ch[i], ch[i + 1], ch[j]) < Cross(ch[i], ch[i + 1], ch[j + 1]))
j = (j + 1) % m;
ans = max(ans,max(Dis(ch[j], ch[i]), Dis(ch[j], ch[i + 1])));
}
printf("%.2lf\n", ans);
}
求凸包的周长
if (n == 1)
printf("0.00\n");
else if (n == 2)
printf("%.2f\n", Dis(ch[0], ch[1]));
else{
double ans = 0;//代表周长
ch[m] = ch[0];
for (int i = 1; i <= m; i++)
ans += Dis(ch[i], ch[i - 1]);
printf("%.2f\n", ans);
}
二:判断直线和直线的位置关系
#include<iostream>
#include<algorithm>
using namespace std;
struct Point
{
int x, y;
};
struct Line
{
int a, b, c;
};
int line(int x1, int y1, int x2, int y2) //向量(x1,y1),(x2,y2);
{
return x1 * y2 - y1 * x2;
}
Line lineform(int x1, int y1, int x2, int y2) //ax + by + c = 0;
//构造直线,返回这条直线的类型
{
Line temp;
temp.a = y2 - y1;
temp.b = x1 - x2;
temp.c = x2 * y1 - x1 * y2;
return temp;
}
double x, y;
void lineintersect(Line l1, Line l2) //求两条直线的交点
{
double d = l1.a * l2.b - l2.a * l1.b;
x = (l2.c * l1.b - l1.c * l2.b) / d;
y = (l2.a * l1.c - l1.a * l2.c) / d;
}
bool gongxian(Point p1, Point p2, Point p3,Point p4) {//判断是否共线
if (line(p2.x - p1.x, p2.y - p1.y, p3.x - p1.x, p3.y - p1.y) == 0 && line(p2.x - p1.x, p2.y - p1.y, p4.x - p1.x, p4.y - p1.y) == 0)
return true;
return false;
}
bool pinxing(Point p1, Point p2, Point p3, Point p4) {//判断是否平行
if (line(p1.x - p2.x, p1.y - p2.y, p3.x - p4.x, p3.y - p4.y) == 0)
return true;
return false;
}
随笔语录
//判断直线类型的步骤
1:首先判断两直线是否共线
2:再判断两直线是否平行
3:如果前面两个条件都不满足的话,就一定有交点,先求出两条直线的一半式方程,然后求两直线的交点