【题解】「UVA681」Convex Hull Finding
更改了一下程序的错误。
Translation
找出凸包,然后逆时针输出每个点,测试数据中没有相邻的边是共线的。多测。
Solution
首先推销一下作者的笔记 由此进入>>> (
明显是一道二维凸包模板。
在这里,我们简单讲一下二维凸包。
「
在平面上能包含所有给定点的最小凸多边形叫做凸包。
其定义为:对于给定集合 \(X\) ,所有包含 \(X\) 的凸集的交集 \(S\) 被称为 \(X\) 的 凸包 。
\(\qquad\qquad\) —— OI-Wiki
」
其实我们可以把凸包看成一个拿橡皮筋围成的一个图形。
假设有一个布满小凸起的板子:
我们要把这些凸起都围起来,怎么围呢?
显然,最简单方变的方法是这样:
但是,我们知道,橡皮筋是有弹力的,所以橡皮筋会往里缩,直到这样:
最外圈的凸起撑起了橡皮筋。
此时橡皮筋围成的多边形的顶点就是最外圈凸起所在的位置。
由此,我们就定义橡皮筋围成的图形为一个平面凸包。
那么,换一种定义,就为:
平面凸包是指覆盖平面上 \(n\) 个点的最小的凸多边形。
当然,我们发现在程序中却无法模拟橡皮筋收缩的过程,于是有了下文的诞生。
二维凸包的求法
斜率逼近法
其实这也是一种容易想到的算法,但是并不常用(代码复杂度高),所以我们省略带过。
Jarvis 算法
这其实是一种数学构造法
此算法的时间复杂度为 \(O(nm)\)。
但是,这个复杂度会爆炸。
于是我们伟大的 \(\sf OIer\) 就想到了 Graham 算法。
Graham 算法
Graham 算法的本质:
Graham 扫描算法维护一个凸壳,通过不断在凸壳中加入新的点和去除影响凸性的点,最后形成凸包。
凸壳:凸包的一部分。
此算法主要分为两部分:
- 排序
- 扫描
排序
我们先选择一个 \(y\) 最小的点(如 \(y\) 相同选 \(x\) 最小),记为 \(p_1\)。
剩下的点,按照极角的大小逆时针排序,记为 \(p_1,p_2,\dots, p_m\)。
我们按照排序结束时的顺序枚举每一个点,依次连线,这里可以使用一个栈来存储,每次入栈,如果即将入栈的元素与栈顶两个元素所构成了一个类似于凹壳的东西,那么显然处于顶点的那个点一定不在这个点集的凸包上,而他正好在栈顶,所以把它弹出栈,新点入栈。
扫描
(下列所说的左右等是指以上一条连线为铅垂线,新的连线偏移的方向)
刚开始,我们的点集是这样的:
\(p_1\) 为起始点。
随后,\(p_2\) 准备入栈,由于栈元素很少,所以可以入栈。
再看 \(p_3\),因为 \(p_3\) 向左,符合凸包条件,入栈。
随后 \(p_4\) 也一切正常,依然向左,入栈。
\(p_5\) 依然向左,入栈。
到 \(p_6\) 时,我们发现了点问题,就是不再是向左了,而是向右了,所以我们此时要将 \(p_5\) 出栈,\(p_6\) 入栈。
入栈后,我们发现,相对于 \(p_4\),\(p_6\) 依然是向右的,所以我们还要把 \(p_4\) 出栈,\(p_6\) 入栈。
接下来 \(p_7\) 没有问题。
\(p_8\) 时,我们发现,也是向右的,所以将 \(p_7\) 出栈,\(p_8\) 入栈。
接下来 \(p_9\) 正常,入栈。
最后,我们再把最后一个点和第一个点连起来。
此时,我们的 Graham 算法的全过程就结束了。
扫描的时间复杂度:\(O(n)\)。
但是不可能那么优秀,还要加上排序的时间复杂度:\(O(n\log n)\)。
所以总时间复杂度为 \(O(n \log n)\)。
可见此算法相比之前的算法优秀的多。
这道题,我们就用 Graham 算法求凸包。
直接按照 Graham 算法思路一步一步走即可。
Code
/*
Problem:UVA681
Date:02/07/20 14:56
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#define line cout << endl
using namespace std;
const int NR = 1e6 + 5;
int t;
int n;
struct point {
int x, y;
};
point p[NR], ps[NR];
double dis (point pa, point pb) {
return sqrt (1.0 * (pb.x - pa.x) * (pb.x - pa.x) + 1.0 * (pb.y - pa.y) * (pb.y - pa.y));
}
int cp (point pa1, point pa2, point pb1, point pb2) {
return (pa2.x - pa1.x) * (pb2.y - pb1.y) - (pb2.x - pb1.x) * (pa2.y - pa1.y);
}
bool cmp (point px, point py) {
if (px.x == py.x && px.y == py.y) return false;
int num = cp (p[1], px, p[1], py);
if (num > 0) return true;
if (num == 0 && dis (p[0], px) < dis (p[0], py)) return true;
return false;
}
int convex_hull () {
sort (p + 2, p + n + 1, cmp);
ps[1] = p[1];
int h = 1;
for (int i = 2; i <= n; i++) {
while (h > 1 && cp (ps[h - 1], ps[h], ps[h], p[i]) <= 0) {
h--;
}
h++;
ps[h] = p[i];
}
ps[h + 1] = p[1];
return h;
}
void _memset () {
memset (p, 0, sizeof p);
memset (ps, 0, sizeof ps);
return;
}
int main () {
// freopen ("UVA681.in", "r", stdin);
// freopen ("UVA681.out", "w", stdout);
cin >> t;
cout << t << endl;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i].x >> p[i].y;
if(i != 1 && p[i].y < p[1].y) {
swap (p[i].y, p[1].y);
swap (p[i].x, p[1].x);
}
}
if (t >= 1) {
int a;
cin >> a;
}
int h = convex_hull ();
cout << h + 1 << endl;
for (int i = 1; i <= h; i++) {
cout << ps[i].x << " " << ps[i].y << endl;
}
cout << ps[1].x << " " << ps[1].y << endl;
if (t >= 1) {
cout << "-1" << endl;
}
_memset ();
}
return 0;
}
【题解】「UVA681」Convex Hull Finding的更多相关文章
- 【题解】「MCOI-02」Convex Hull 凸包
题目戳我 \(\text{Solution:}\) \[\sum_{i=1}^n \sum_{j=1}^n \rho(i)\rho(j)\rho(\gcd(i,j)) \] \[=\sum_{d=1} ...
- 【题解】「UVA11626」Convex Hull
凸包模板题. 之前写过拿 Graham 算法求凸包的,为了不重复/多学点知识,那这次拿 Andrew 算法求凸包吧qaq *此文章所有图片均为作者手画. Andrew 算法 假设我们有这些点: 首先把 ...
- P6810 「MCOI-02」Convex Hull 凸包
Link 一句话题意: 求出 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\tau(i)\tau(j)\tau(gcd(i,j))\) 前置知识 \(diri ...
- 题解 「HDU6403」卡片游戏
link Description 桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏.如今卡牌还在,她却不在我身边.不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间. 每张卡牌的正面与反面都各有 ...
- 题解 「SCOI2016」萌萌哒
link Description 一个长度为 $ n $ 的大数,用 $ S_1S_2S_3 \ldots S_n $表示,其中 $ S_i $ 表示数的第 $ i $ 位,$ S_1 $ 是数的最高 ...
- 题解 「SDOI2017」硬币游戏
题目传送门 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强 ...
- 题解 「ZJOI2018」历史
题目传送门 Description 九条可怜是一个热爱阅读的女孩子. 这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣. 这个世界有 \(n\) 个城市,这 \(n\) 个城市被恰 ...
- 题解 「BZOJ3636」教义问答手册
题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...
- 题解「BZOJ4310」跳蚤
题目传送门 Description 现在有一个长度为 \(n\) 的字符串,将其划分为 \(k\) 段,使得这 \(k\) 段每一段的字典序最大子串中字典序最大的字符串字典序尽量小.求出这个字符串. ...
随机推荐
- 进程 PCB 进程挂起
7-1 进程定义 OS系统从只能跑一个程序到能跑多个.进程可以描述程序的执行过程. 进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程. 只有当一个程序被OS加载到内存中,cpu对其 ...
- UNP——第二章,端口号,套接字对,TCP,UDP输出
1.端口号 端口号用于区分使用相同协议的进程. TCP69 与 UDP69 是不同的. 端口号范围 0 - 65535, 其中 0- 1023 是保留端口. 2.套接字对 TCP服务通过套接字对,唯一 ...
- BurpSuite多重代理的情形
有时候为了隐藏自己的真实IP,访问目标网站需要走代理.比如说,通过代理IP访问目标网站并且代理IP可以随时切换,这样可以避免IP被封堵后无法访问目标网站的尴尬. 一. 首先把代理切换为全局模式 操作步 ...
- 总是说spring难学?来看完这些spring的注解及其解释,真香!
前言 用过spring的人都知道,spring简单的通过注解就可以完成很多事情,但这些东西是如何实现的呢以及如何应用到我们自己的代码中?接下来,让我们一起开启注解的旅程. 1. @Controller ...
- MathType单边大括号的编辑技巧你知道吗?
大家都知道,一般情况下,数学里面的括号都是成对出现的,但是也有些情况下可以只用到单边的括号,就比如分段函数,在编写的时候只需用到左半边的括号.MathType作为专业的公式编辑器,用它来编写公式再方便 ...
- ppt-页面大小和颜色更改
1.ppt大小设置:设计--幻灯片大小--可选择标准.宽屏或自定义幻灯片大小--下拉 幻灯片大小可选择其他尺寸的幻灯片 2.设置图片背景 设计--设置背景格式--依次有纯色.渐变.图案等填充 2.1 ...
- Spring Boot第一天
1.首先在idea中创建一个maven项目,创建成功后在pom.xml中添加SpringBoot相关的依赖 <!--引入SpringBoot相关的依赖--> <parent> ...
- mysql 数据文件
mysql8.0取消了frm文件 . ibd数据和索引
- 创建topic
sh kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic ...
- 【mq读书笔记】消息消费队列和索引文件的更新
ConsumeQueue,IndexFile需要及时更新,否则无法及时被消费,根据消息属性查找消息也会出现较大延迟. mq通过开启一个线程ReputMessageService来准时转发commitL ...