更改了一下程序的错误。

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的更多相关文章

  1. 【题解】「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} ...

  2. 【题解】「UVA11626」Convex Hull

    凸包模板题. 之前写过拿 Graham 算法求凸包的,为了不重复/多学点知识,那这次拿 Andrew 算法求凸包吧qaq *此文章所有图片均为作者手画. Andrew 算法 假设我们有这些点: 首先把 ...

  3. P6810 「MCOI-02」Convex Hull 凸包

    Link 一句话题意: 求出 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}\tau(i)\tau(j)\tau(gcd(i,j))\) 前置知识 \(diri ...

  4. 题解 「HDU6403」卡片游戏

    link Description 桌面上摊开着一些卡牌,这是她平时很爱玩的一个游戏.如今卡牌还在,她却不在我身边.不知不觉,我翻开了卡牌,回忆起了当时一起玩卡牌的那段时间. 每张卡牌的正面与反面都各有 ...

  5. 题解 「SCOI2016」萌萌哒

    link Description 一个长度为 $ n $ 的大数,用 $ S_1S_2S_3 \ldots S_n $表示,其中 $ S_i $ 表示数的第 $ i $ 位,$ S_1 $ 是数的最高 ...

  6. 题解 「SDOI2017」硬币游戏

    题目传送门 Description 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强 ...

  7. 题解 「ZJOI2018」历史

    题目传送门 Description 九条可怜是一个热爱阅读的女孩子. 这段时间,她看了一本非常有趣的小说,这本小说的架空世界引起了她的兴趣. 这个世界有 \(n\) 个城市,这 \(n\) 个城市被恰 ...

  8. 题解 「BZOJ3636」教义问答手册

    题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...

  9. 题解「BZOJ4310」跳蚤

    题目传送门 Description 现在有一个长度为 \(n\) 的字符串,将其划分为 \(k\) 段,使得这 \(k\) 段每一段的字典序最大子串中字典序最大的字符串字典序尽量小.求出这个字符串. ...

随机推荐

  1. linux系统中离线安装python3.7过程记录

    最近公司新弄来一台linux  redhat 4.4.7服务器,准备在上面离线安装python3.7,安装过程中出现一些问题,特此记录下来. 首先在python官网上下载了 Python-3.7.3. ...

  2. 从头学起Verilog(二):时序逻辑基础与回顾

    引言 时序逻辑对于数字电路设计十分重要,本文针对数字电路中的时序逻辑部分进行了系统的回顾. 存储器件 由于时序逻辑的输出不但受当前输入影响,还受之前的输入的影响,所以需要有存储单元对以前的输入进行存储 ...

  3. Spring源码之FactoryBean的实现

    https://zhuanlan.zhihu.com/p/97005407 https://blog.csdn.net/qq_35634181/article/details/104507465 总结 ...

  4. Python生成csv中文乱码解决办法

    前言 在Linux下面用python进行数据处理,然后输出为csv格式,如果没有中文一切正常,但是如果有中文,就会出现乱码的问题,本篇将讲述怎么处理这个问题 处理过程 原始代码 #!/usr/bin/ ...

  5. Kubernetes 入门与安装部署

    一.简介 参考:Kubernetes 官方文档.Kubernetes中文社区 | 中文文档 Kubernetes 是一个可移植的.可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自 ...

  6. Docker学习—Swarm

    前言: 前一篇<Docker学习-Machine>中对Machine 进行了学习,本篇继续学习Swarm,那么Swarm是什么呢,有什么用呢?接下来一步步了解. 一.什么是Docker-S ...

  7. CTF-WEB-XTCTF-Web_php_unserialize

    题目来源 XTCTF-Web_php_unserialize 题目考点:PHP代码审计.PHP正则.PHP序列化与反序列化 解题思路 题目源码 <?php class Demo { privat ...

  8. mysql学习——数据库基本操作

    查看当前数据库 创建数据库 查看数据库定义 删除数据库

  9. 二叉堆python实现

    二叉堆是一种完全二叉树,我们可以使用列表来方便存储,也就是说,用列表将树的所有节点存储起来. 如下图,是小根堆方式的二叉堆,假设父节点的下标为p,则他的左孩子下标为2P+1,右孩子下标为2P+2 cl ...

  10. iMindMap不同视图的应用技巧介绍

    在刚开始使用iMindMap思维导图软件时,很多用户会习惯性地使用默认的Mind Map视图.因该视图布局自由,用户可以发挥自我创造力,进行多种形式的思维图表创建. 其实,除此之外,iMindMap还 ...