挑战程序竞赛系列(90):3.6凸包(1)
2017-09-29 18:32
603 查看
挑战程序竞赛系列(90):3.6凸包(1)
传送门:POJ 2187: Beauty Contest题意:
平面上有N个牧场。i号牧场的位置在格点(xi,yi),所有牧场的位置互不相同。请计算距离最远的两个牧场的距离,输出最远距离的平方。
假设有四个点,其中一个点在三个点的内部,可以知道该点是冗余的,所以我们只需要维护所有点的凸包,求出凸包上点集中的两两最大即可。
凸包的详细介绍可以参考博文:
http://blog.csdn.net/u014688145/article/details/72200018#t3
暴力枚举点对
代码如下:import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.StringTokenizer; public class Main{ String INPUT = "./data/judge/201709/P2187.txt"; public static void main(String[] args) throws IOException { new Main().run(); } static final int MAX_N = 50000; class Point implements Comparable<Point>{ int x; int y; Point(int x, int y){ this.x = x; this.y = y; } Point sub(Point a) { return new Point(x - a.x, y - a.y); } int det(Point a) { return x * a.y - y * a.x; } @Override public int compareTo(Point o) { return this.x != o.x ? this.x - o.x : this.y - o.y; } } int N; Point[] ps; List<Point> convexHull(){ Arrays.sort(ps); List<Point> ans = new ArrayList<Point>(); int[] stack = new int[2 * N]; int tot = 0; for (int i = 0; i < N; ++i) { while (tot > 1 && ps[stack[tot - 1]].sub(ps[stack[tot - 2]]).det(ps[i].sub(ps[stack[tot - 1]])) <= 0) tot--; stack[tot++] = i; } for (int i = N - 2, t = tot; i >= 0; --i) { while (tot > t && ps[stack[tot - 1]].sub(ps[stack[tot - 2]]).det(ps[i].sub(ps[stack[tot - 1]])) <= 0) tot--; stack[tot++] = i; } tot --; for (int i = 0; i < tot; ++i) { ans.add(ps[stack[i]]); } return ans; } int dist(Point a, Point b) { int dx = a.x - b.x; int dy = a.y - b.y; return dx * dx + dy * dy; } void solve() { List<Point> qs = convexHull(); int max = 0; for (int i = 0; i < qs.size(); ++i) { for (int j = i + 1; j < qs.size(); ++j) { max = Math.max(max, dist(qs.get(i), qs.get(j))); } } out.println(max); } void read() { N = ni(); ps = new Point ; for (int i = 0; i < N; ++i) { ps[i] = new Point(ni(), ni()); } solve(); } FastScanner in; PrintWriter out; void run() throws IOException { boolean oj; try { oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode"); } catch (Exception e) { oj = System.getProperty("ONLINE_JUDGE") != null; } InputStream is = oj ? System.in : new FileInputStream(new File(INPUT)); in = new FastScanner(is); out = new PrintWriter(System.out); long s = System.currentTimeMillis(); read(); out.flush(); if (!oj){ System.out.println("[" + (System.currentTimeMillis() - s) + "ms]"); } } public boolean more(){ return in.hasNext(); } public int ni(){ return in.nextInt(); } public long nl(){ return in.nextLong(); } public double nd(){ return in.nextDouble(); } public String ns(){ return in.nextString(); } public char nc(){ return in.nextChar(); } class FastScanner { BufferedReader br; StringTokenizer st; boolean hasNext; public FastScanner(InputStream is) throws IOException { br = new BufferedReader(new InputStreamReader(is)); hasNext = true; } public String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (Exception e) { hasNext = false; return "##"; } } return st.nextToken(); } String next = null; public boolean hasNext(){ next = nextToken(); return hasNext; } public int nextInt() { if (next == null){ hasNext(); } String more = next; next = null; return Integer.parseInt(more); } public long nextLong() { if (next == null){ hasNext(); } String more = next; next = null; return Long.parseLong(more); } public double nextDouble() { if (next == null){ hasNext(); } String more = next; next = null; return Double.parseDouble(more); } public String nextString(){ if (next == null){ hasNext(); } String more = next; next = null; return more; } public char nextChar(){ if (next == null){ hasNext(); } String more = next; next = null; return more.charAt(0); } } }
旋转卡壳法
参考博文:http://www.cnblogs.com/DreamUp/archive/2010/09/16/1828131.html
参考《挑战》P263,借用书上的解释,和上篇博文的一张图:
实际上,最远点对一定在所有对踵点对上,所以只需要枚举出凸包上的对踵点对即能高效求解最远点对。
那么假设我们知道了对踵点对,qa和qb,如何求下一个对踵点对呢?考虑qa和qa的下一个顶点,qb和qb的下一顶点,可以画出两条向量,并且以qa为原点,做qb和qb的下一顶点的平行线,如下:
于是根据叉积可以得到两向量的相对位置关系(谁在最外侧),如果平移后的线在内侧,则说明对踵点对为qb和qa的next,反之为qb的next和qa。
有了上述关系,就可以直接根据对踵点对更新最远点对了,而我们知道在对踵点对内部的点是不可能为最远点对的,这是复杂度降低的真正原因。
代码如下:
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Arrays; import java.util.StringTokenizer; public class Main{ String INPUT = "./data/judge/201710/P2187.txt"; public static void main(String[] args) throws IOException { new Main().run(); } static final int MAX_N = 50000 + 16; int N; class P implements Comparable<P>{ int x; int y; P(int x, int y){ this.x = x; this.y = y; } P sub(P a) { return new P(x - a.x, y - a.y); } int det(P a) { return x * a.y - y * a.x; } @Override public int compareTo(P o) { return x != o.x ? x - o.x : y - o.y; } } P[] ps; int dist(P a, P b) { int dx = a.x - b.x; int dy = a.y - b.y; return dx * dx + dy * dy; } P[] convexHull() { P[] qs = new P[2 * N]; Arrays.sort(ps); int k = 0; for (int i = 0; i < N; ++i) { while (k > 1 && qs[k - 1].sub(qs[k - 2]).det(ps[i].sub(qs[k - 1])) <= 0) k--; qs[k++] = ps[i]; } for (int i = N - 2, t = k; i >= 0; --i) { while (k > t && qs[k - 1].sub(qs[k - 2]).det(ps[i].sub(qs[k - 1])) <= 0) k--; qs[k++] = ps[i]; } k --; P[] res = new P[k]; System.arraycopy(qs, 0, res, 0, k); return res; } void solve() { P[] qs = convexHull(); int n = qs.length; if (n == 2) { out.println(dist(qs[0], qs[1])); return; } int i = 0; int j = 0; for (int k = 1; k < n; ++k) { if (qs[k].x < qs[i].x) i = k; if (qs[k].x > qs[j].x) j = k; } int si = i; int sj = j; int max = 0; while (i != sj || j != si) { max = Math.max(max, dist(qs[i], qs[j])); if (qs[(i + 1) % n].sub(qs[i]).det(qs[(j + 1) % n].sub(qs[j])) < 0) { i = (i + 1) % n; } else { j = (j + 1) % n; } } out.println(max); } void read() { N = ni(); ps = new P ; for (int i = 0; i < N; ++i) { ps[i] = new P(ni(), ni()); } solve(); } FastScanner in; PrintWriter out; void run() throws IOException { boolean oj; try { oj = ! System.getProperty("user.dir").equals("F:\\oxygen_workspace\\Algorithm"); } catch (Exception e) { oj = System.getProperty("ONLINE_JUDGE") != null; } InputStream is = oj ? System.in : new FileInputStream(new File(INPUT)); in = new FastScanner(is); out = new PrintWriter(System.out); long s = System.currentTimeMillis(); read(); out.flush(); if (!oj){ System.out.println("[" + (System.currentTimeMillis() - s) + "ms]"); } } public boolean more(){ return in.hasNext(); } public int ni(){ return in.nextInt(); } public long nl(){ return in.nextLong(); } public double nd(){ return in.nextDouble(); } public String ns(){ return in.nextString(); } public char nc(){ return in.nextChar(); } class FastScanner { BufferedReader br; StringTokenizer st; boolean hasNext; public FastScanner(InputStream is) throws IOException { br = new BufferedReader(new InputStreamReader(is)); hasNext = true; } public String nextToken() { while (st == null || !st.hasMoreTokens()) { try { st = new StringTokenizer(br.readLine()); } catch (Exception e) { hasNext = false; return "##"; } } return st.nextToken(); } String next = null; public boolean hasNext(){ next = nextToken(); return hasNext; } public int nextInt() { if (next == null){ hasNext(); } String more = next; next = null; return Integer.parseInt(more); } public long nextLong() { if (next == null){ hasNext(); } String more = next; next = null; return Long.parseLong(more); } public double nextDouble() { if (next == null){ hasNext(); } String more = next; next = null; return Double.parseDouble(more); } public String nextString(){ if (next == null){ hasNext(); } String more = next; next = null; return more; } public char nextChar(){ if (next == null){ hasNext(); } String more = next; next = null; return more.charAt(0); } } }
相关文章推荐
- 挑战程序竞赛系列(94):3.6凸包(5)
- 挑战程序竞赛系列(93):3.6凸包(4)
- 挑战程序竞赛系列(91):3.6凸包(2)
- 挑战程序竞赛系列(92):3.6凸包(3)
- 挑战程序竞赛系列(58):4.6树上的分治法(1)
- 挑战程序竞赛系列(59):4.6树上的分治法(2)
- 挑战程序竞赛系列(3):2.3需要思考的动规
- 挑战程序竞赛系列(36):3.3线段树和平方分割
- 挑战程序竞赛系列(52):4.2 Nim 与 Grundy 数
- 挑战程序竞赛系列(10):2.4并查集
- 挑战程序竞赛系列(62):4.6平面上的分治法(2)
- 挑战程序竞赛系列(18):3.1查找第k大的值
- 挑战程序竞赛系列(64):4.7字符串上的动态规划(2)
- 挑战程序竞赛系列(19):3.1最小化第k大的值
- 挑战程序竞赛系列(23):3.2折半枚举
- 挑战程序竞赛系列(41):4.1中国剩余定理
- 挑战程序竞赛系列(53):4.4 栈
- 挑战程序竞赛系列(56):4.4 双端队列(3)
- 挑战程序竞赛系列(26):3.5二分图匹配(1)
- 挑战程序竞赛系列(2):2.3优化递推关系式