POJ2187(凸包+旋转卡壳)
这道题目的大意是给出一组二维空间的顶点,计算其中距离最远的两个顶点之间的距离。
先说明凸包的概念和求法。
定义:对于多边形P,若将P中任意的两个点(包含边上)用一条线段连接,线段都落于该多边形中(含边),那么该多边形称为凸多边形。
定义:点集Q的凸包是一个最小的凸多边形P,使得Q中的每个点都落于P中或P的边上。
形象的看,可以将点集Q看作是一组钉在平面上的钉子,而利用一个橡皮圈将整个点集Q套入其中,使得橡皮圈绷直,那么橡皮圈实际上就是点集Q的凸包的外轮廓。
假设点集Q的凸包为P,将P所有角按照逆时针排序,得到一组点序列q1,q2,...,qn。实际上P的所有角必定是Q中的点,否则可以通过旋转点的两边,使得两边交点为Q中的点,同时保证凸包性质。可以同样保证对于凸多变形,若其所有角被Q覆盖,那么必定是Q的凸包,因为我们没有办法继续缩减其大小。q1q3向量一定在q1q2向量的逆时针方向,否则连接q1和q2得到的向量将会落于凸包外。利用这个思路可以实现一个有效的Graham扫描算法,其可以在O(nlog2(n))的时间复杂度内建立点集的凸包,n为点集的大小。
首先取Q中y坐标最小的点O(若有多个,则取其中x坐标最小的)。之后我们以O为原点建立极坐标系,可以发现所有其余顶点的极角都属于[0,180)。之后对其余顶点按照其极角从小到大排序(这里不需要真的计算角度,只需要利用向量的叉积判断向量之间的旋转关系即可),排序后,我们整合所有极角相同的顶点,只保留其中距离v最远的顶点。最后我们对排序的顶点集合L,进行下面的步骤
stack = empty-stack
stack.push(v)
for point in L
)
) //叉乘运算
stack.pop()
else
break
stack.push(point)
最终保留在stack中的就是Q的凸包的按照逆时针排序的角。在算法一开始时,显然vUL中的顶点必定覆盖了凸包的所有角。在扫描每个点z时,若发现对于前面两个顶点y和x,有xz落在xy的顺时针方向或二者共线,那么从vUL中移除点y,依旧可以保证剩余顶点覆盖凸包所有角(注意v,x,z三个顶点组成的三角形已经包含了y)。因此到了最后stack中剩余的顶点必定覆盖了凸包的所有角。并且由于stack所有顶点都满足了不能被移除的性质,因此得到的就是凸包上按逆时针排序的角。
时间复杂度为排序和上述扫描过程,排序为O(nlog2(n)),而扫描由于每个点最多一次入栈和一次出栈,而每次while循环都会发生一次出栈,因此while总共最多运行O(n)次,故扫描整个过程时间复杂度为O(n),总的时间复杂度取决于排序,为O(nlog2(n))。
最远的两个顶点必定是落在凸包上的,若其中之一落在凸包内,那么可以将两个点相连,得到线段,并向两端扩展直到触碰到凸包轮廓,而这样的一条边是必定小于凸包上的某两个顶点的连线的,稍微运用一点高中几何的知识就可以证明。到了这里,问题还是没有解决,若问题给出所有顶点都是类似椭圆边上的顶点,那么凸包就将由所有顶点组成,这样计算凸包并不能简化对最远两个顶点之间距离的计算。
可以使用旋转卡壳算法优化这一过程(Rotating Calipers Algorithm)。对于凸包上的某条边(a,b),对于所有凸包上的顶点v,若abc是所有类似的以ab为底的三角形中面积最大的,那么称a与v以及b与v均是对踵点。
要找边(a,b)的对踵点,最直观的方式是对建立其平行线并找到凸包上另外一个与该平行线相切的交点(凸包上最多有两个切点,且这两个切点必定相连为边),该交点就是边(a,b)的对踵点。利用图形可以发现当我们选取下一条边(b,c)找寻其对踵点时(设a,b,c处于顺时针方向),(b,c)的对踵点必定为(a,b)的对踵点,或落于(a,b)的对踵点的顺时针方向。且对于任意一组边(a,b),以及b逆时针方向的顶点序列,c,d,e,...,可以发现其与(a,b)组成的三角形的面积是先增大后减小的,即存在一个极点,该极点即为(a,b)的对踵点。因此我们可以保证(b,c)的对踵点落于b的逆时针方向,且落于(a,b)的对踵点的顺时针方向,但绝不可能是b或c。因此当我们按顺时针扫描了凸包上的所有边后,我们对对踵点的查找最多绕了凸包两圈,即时间复杂度为O(n),n为凸包上的顶点数。
i = , j =
n = convex.size()
convex[n] = convex[]
result = empty-list
for( ; j < n; j++)
], convex[i % n])
< area(convex[j], convex[j + ], convex[(i + ) % n])) //计算面积差
i +=
result.add(Pair(convex[j], convex[i % n]))
result.add(Pair(convex[j + ], convex[i % n]))
], convex[i % n])
== area(convex[j], convex[j + ], convex[(i + ) % n])) //边有两个对踵点
result.add(Pair(convex[j], convex[(i + ) % n]))
result.add(Pair(convex[j + ], convex[(i + ) % n]))
上面就是利用RC计算所有对踵点对的代码了。由于每条边最多带来4个对踵点对,因此可以保证结果中的对踵点对不会超过4n。
但是说了这么多,计算对踵点对对于我们的问题有什么帮助呢?考虑a和b是最远的两个顶点,我们可以做两条平行线同时与凸包相切于点a和点b。之后旋转按任意方向旋转平行线,直到平行线于凸包的某个边重合。此时不妨设a为重合边的一端,显然此时b是a的对踵点。因此我们发现了凸包上的最远点对必定是对踵点,故我们只要遍历计算最多4n个对踵点对各自距离中的最大值,就是我们要的结果。
到此累计的时间复杂度为O(nlog2(n))。
package cn.dalt.poj;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Created by dalt on 2017/12/11.
*/
public class BeautyContest {
static BlockReader reader;
public static void main(String[] args) throws Exception {
System.setIn(new FileInputStream("D:\\test\\poj\\BeautyContest.in"));
reader = new BlockReader(System.in);
while (reader.hasMore()) {
BeautyContest beautyContest = new BeautyContest();
beautyContest.init();
System.out.println(beautyContest.solve());
}
}
List<Vector2I> farmPositions;
public void init() {
int n = reader.nextInteger();
farmPositions = new ArrayList();
for (int i = 0; i < n; i++) {
farmPositions.add(new Vector2I(
reader.nextInteger(), reader.nextInteger()
));
}
}
public int solve() {
Convex convex = Convex.makeConvex(farmPositions);
if (convex.size() <= 2) {
return GeomUtils.dist2(convex.getBottomLeftCorner(), convex.getTopRightCorner());
}
List<Vector2I[]> antipodalPairList = shamos(convex);
int ret = 0;
for (Vector2I[] pair : antipodalPairList) {
ret = Math.max(ret, GeomUtils.dist2(pair[0], pair[1]));
}
return ret;
}
public static class Vector2I {
final int x;
final int y;
public Vector2I(int x, int y) {
this.x = x;
this.y = y;
}
public Vector2I sub(Vector2I other) {
return new Vector2I(x - other.x, y - other.y);
}
public String toString() {
return String.format("(%d,%d)", x, y);
}
public boolean equals(Object obj) {
Vector2I vec = (Vector2I) obj;
return x == vec.x && y == vec.y;
}
}
public static class BlockReader {
InputStream is;
byte[] dBuf;
int dPos, dSize, next;
static final int EOF = -1;
public void skipBlank() {
while (Character.isWhitespace(next)) {
next = nextByte();
}
}
StringBuilder builder = new StringBuilder();
public String nextBlock() {
builder.setLength(0);
skipBlank();
while (next != EOF && !Character.isWhitespace(next)) {
builder.append((char) next);
next = nextByte();
}
return builder.toString();
}
public int nextInteger() {
skipBlank();
int ret = 0;
boolean rev = false;
if (next == '+' || next == '-') {
rev = next == '-';
next = nextByte();
}
while (next >= '0' && next <= '9') {
ret = (ret << 3) + (ret << 1) + next - '0';
next = nextByte();
}
return rev ? -ret : ret;
}
public int nextBlock(char[] data, int offset) {
skipBlank();
int index = offset;
int bound = data.length;
while (next != EOF && index < bound && !Character.isWhitespace(next)) {
data[index++] = (char) next;
next = nextByte();
}
return index - offset;
}
public boolean hasMore() {
skipBlank();
return next != EOF;
}
public BlockReader(InputStream is) {
this(is, 1024);
}
public BlockReader(InputStream is, int bufSize) {
this.is = is;
dBuf = new byte[bufSize];
next = nextByte();
}
public int nextByte() {
while (dPos >= dSize) {
if (dSize == -1) {
return EOF;
}
dPos = 0;
try {
dSize = is.read(dBuf);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return dBuf[dPos++];
}
}
public static class GeomUtils {
private GeomUtils() {
}
public static int dist2(Vector2I a, Vector2I b) {
int x = a.x - b.x;
int y = a.y - b.y;
return x * x + y * y;
}
public static boolean sameLine(Vector2I a, Vector2I b, Vector2I c) {
return cmul(b.sub(a), c.sub(a)) == 0;
}
public static int cmul(Vector2I a, Vector2I b) {
return a.x * b.y - a.y * b.x;
}
public static int rectArea(Vector2I a, Vector2I b, Vector2I c) {
return Math.abs(cmul(a.sub(c), b.sub(c)));
}
}
public static List<Vector2I[]> shamos(List<Vector2I> convex) {
//Rotating calipers
//Fetch all antipodal pair
int convexNum = convex.size();
int vertexIndex = 2;
convex = new ArrayList(convex);
convex.add(convex.get(0));
List<Vector2I[]> antipodalPairList = new ArrayList();
for (int i = 0; i < convexNum; i++) {
Vector2I edgePart1 = convex.get(i);
Vector2I edgePart2 = convex.get(i + 1);
while (true) {
Vector2I scannedVector = convex.get(vertexIndex);
Vector2I nextVector = convex.get(vertexIndex + 1);
int areaDiff = GeomUtils.rectArea(edgePart1, edgePart2, scannedVector) - GeomUtils.rectArea(edgePart1, edgePart2, nextVector);
if (areaDiff < 0) {
if (++vertexIndex >= convexNum) {
vertexIndex = 0;
}
} else {
antipodalPairList.add(new Vector2I[]{edgePart1, scannedVector});
antipodalPairList.add(new Vector2I[]{edgePart2, scannedVector});
if (areaDiff == 0) {
antipodalPairList.add(new Vector2I[]{edgePart1, nextVector});
antipodalPairList.add(new Vector2I[]{edgePart2, nextVector});
}
break;
}
}
}
return antipodalPairList;
}
public static class Convex extends AbstractList<Vector2I> {
private Vector2I[] vectors;
private Vector2I bl;
private Vector2I tr;
public Vector2I getBottomLeftCorner() {
return bl;
}
public Vector2I getTopRightCorner() {
return tr;
}
@Override
public Vector2I get(int index) {
return vectors[index];
}
public int size() {
return vectors.length;
}
public static Convex makeConvex(List<Vector2I> vector2IList) {
Convex result = new Convex();
if (vector2IList.size() == 0) {
result.vectors = vector2IList.toArray(new Vector2I[0]);
return result;
}
//If all points located on same line
Vector2I v1 = vector2IList.get(0);
Vector2I v2 = vector2IList.get(0);
Vector2I bl = vector2IList.get(0);
Vector2I tr = vector2IList.get(0);
boolean sameLineFlag = true;
for (Vector2I vertex : vector2IList) {
if (!GeomUtils.sameLine(v1, v2, vertex)) {
sameLineFlag = false;
}
v2 = vertex;
if (bl.y > vertex.y || (bl.y == vertex.y && bl.x > vertex.x)) {
bl = vertex;
}
if (tr.y < vertex.y || (tr.y == vertex.y && tr.x < vertex.x)) {
tr = vertex;
}
}
result.bl = bl;
result.tr = tr;
if (sameLineFlag) {
if (bl.equals(tr)) {
result.vectors = new Vector2I[]{bl};
} else {
result.vectors = new Vector2I[]{bl, tr};
}
return result;
}
//Remove all inner vertex, make vectors contains points on outline of convex
//At first, sort by angle of vector bl-v
//v < u equals to that bl-u is on the anticlockwise of bl-v
//So we can simplify the procession of calculation because of -cmul(bl-v, bl-u)=v.compareTo(b)
final Vector2I finalBl = bl;
Vector2I[] vector2IListArray = vector2IList.toArray(new Vector2I[vector2IList.size()]);
vector2IList = Arrays.asList(vector2IListArray);
Arrays.sort(vector2IListArray, new Comparator<Vector2I>() {
public int compare(Vector2I a, Vector2I b) {
int res = -GeomUtils.cmul(a.sub(finalBl), b.sub(finalBl));
if (res == 0) {
if (a.equals(finalBl)) {
return -1;
}
if (b.equals(finalBl)) {
return 1;
}
}
return res;
}
});
//Remove all the vertex has the same angle but retain the farthest one
int newSize = 1;
for (int i = 2, bound = vector2IList.size(); i < bound; i++) {
Vector2I candidate = vector2IListArray[newSize];
Vector2I scanOne = vector2IListArray[i];
if (GeomUtils.sameLine(candidate, scanOne, bl)) {
//Retain the farthest one in the vertexes with same angle
//Replace the candidate
if (GeomUtils.dist2(bl, scanOne) > GeomUtils.dist2(bl, candidate)) {
vector2IListArray[newSize] = scanOne;
}
} else {
//Add the candidate
newSize++;
vector2IListArray[newSize] = scanOne;
}
}
vector2IList = vector2IList.subList(0, newSize + 1);
//Graham's Scan
LinkedList<Vector2I> stack = new LinkedList();
for (int i = 0, bound = vector2IList.size(); i < bound; i++) {
Vector2I vec = vector2IList.get(i);
while (stack.size() >= 2) {
Vector2I top1 = stack.removeLast();
Vector2I top2 = stack.getLast();
if (GeomUtils.cmul(top1.sub(top2), vec.sub(top2)) > 0) {
stack.addLast(top1);
break;
}
}
stack.addLast(vec);
}
result.vectors = stack.toArray(new Vector2I[stack.size()]);
return result;
}
}
}
说真的,Graham很难写,需要考虑移除相同极角,考虑全顶点共线,重点等一大堆情况,恶心死了。
POJ2187(凸包+旋转卡壳)的更多相关文章
- [USACO2003][poj2187]Beauty Contest(凸包+旋转卡壳)
http://poj.org/problem?id=2187 题意:老题了,求平面内最远点对(让本渣默默想到了悲剧的AHOI2012……) 分析: nlogn的凸包+旋转卡壳 附:http://www ...
- poj 2079 Triangle (二维凸包旋转卡壳)
Triangle Time Limit: 3000MS Memory Limit: 30000KB 64bit IO Format: %I64d & %I64u Submit Stat ...
- UVA 4728 Squares(凸包+旋转卡壳)
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17267 [思路] 凸包+旋转卡壳 求出凸包,用旋转卡壳算出凸包的直 ...
- Code Chef GEOCHEAT(凸包+旋转卡壳+随机化)
题面 传送门 题解 以下记\(S_i=\{1,2,3,...,i\}\) 我们先用凸包+旋转卡壳求出直径的长度,并记直径的两个端点为\(i,j\)(如果有多条直径随机取两个端点) 因为这个序列被\(r ...
- poj 2187 Beauty Contest(二维凸包旋转卡壳)
D - Beauty Contest Time Limit:3000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u ...
- POJ 2187 凸包+旋转卡壳
思路: 求个凸包 旋转卡壳一下 就求出来最远点对了 注意共线情况 也就是说 凸包如果有一堆点共线保留端点即可 //By SiriusRen #include <cmath> #incl ...
- poj2187 Beauty Contest (凸包 + 旋转卡壳)
Beauty Contest Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 38349 Accepted: 11851 ...
- [POJ2187][BZOJ1069]旋转卡壳
旋转卡壳 到现在依然不确定要怎么读... 以最远点对问题为例,枚举凸包上的两个点是最简单的想法,时间复杂度O(n2) 我们想象用两条平行线卡着这个凸包,当其中一个向某个方向旋转的时候另一个显然也是朝同 ...
- 【BZOJ 1069】【SCOI 2007】最大土地面积 凸包+旋转卡壳
因为凸壳上对踵点的单调性所以旋转卡壳线性绕一圈就可以啦啦啦--- 先求凸包,然后旋转卡壳记录$sum1$和$sum2$,最后统计答案就可以了 #include<cmath> #includ ...
随机推荐
- oracle如何去除字段的回车换行符
oracle如何去除字段的回车换行符? 可以用trim也可以用replace.区别在于,trim处理字符串两端,而replace中间也可以处理. trim select '全世界无产者 ' || '联 ...
- Problem: Query on the tree(二分+划分树)
题目链接: Problem: Query on the tree Time limit: 1s Mem limit: 64 MB Problem Description There ...
- 数据库需要支持emoji表情
由于需要实现emoji表情评论的功能,所以数据库需要支持emoji表情的存储,根据查询的资料最终实现了该功能,现将实现的过程以及过程遇到的一些问题记录下来,供大家参考和交流. mysql的utf8编码 ...
- CVS winCVS配置讲解及用户管理
首先 用到工具包包括 CVSNT ,WinCvs及相关python 2.2.3和TCL832 可点此链接下载 http://files.cnblogs.com/lppblogs/%E6%96%87%E ...
- 解决"hibernate.hbm2ddl.auto" update值 无效
<property name="schemaUpdate"> <value>true</value> </property> 若果是 ...
- Linux 安全rm
先将shell脚本放在某个全局路径下,如/usr/local/bin #!/bin/sh # safe rm # Don't remove the file, just move them to a ...
- fedora 安装新字体 courier new xxx
fedora安装新字体 1.将windows字体拷贝到/usr/share/fonts/truetype下面,文件夹名字可以随便起 cp /media/c/WINDOWS/Fonts/* /usr/s ...
- HDU - 3374:String Problem (最小表示法模板题)
Give you a string with length N, you can generate N strings by left shifts. For example let consider ...
- BZOJ5337 [TJOI2018]str
题意 小豆参加了生物实验室.在实验室里,他主要研究蛋臼质.他现在研究的蛋臼质是由k个氨基酸按一定顺序构成的.每一个氨基酸都可能有a种碱基序 列si_j 构成.现在小豆有一个碱基串s,小豆想知道在这个碱 ...
- Oracle 11G RAC:生产环境下架构
转: it168网站 原创 作者:刘炳林 在真实环境搭建一套Oracle RAC就好比是一堂劳动课,劳动前需要准备好劳动工具,对劳动课内容有充分的认识;按照步骤一步一步进行,需要考虑劳动过程中可能遇 ...