分治法求凸包:
POJ2187求多个点间最长距离,若直接用循环枚举所有线段会超时,直接暴力求解时间复杂度为O(n)。
若先用NlogN的时间求出凸包点集,在对点集中所有点枚举,效率会缩短到O(nlogn)。
求凸包基本思路:
1、找出点集中最靠左和最靠右的点,也就是找到一条能够完全划分点集的主线段。
2、把所有点分成两类,一类在主线段左边,一类在主线段右侧,分别带入divide函数进行分治解决。
3、divide函数参数类型包括代表主线段的两点,和一侧的点集,最好能够保证点集在主线段的左侧,其功能包括用三角形面积公式得到距离主线段最远的点top,再划分两条线p1-top , top-p2,再用三角形面积公式的正负性找出相对应的点集s1,s2,再分别divide
4、如果divide函数参数中set为空,证明p1,p2均为凸包边上的点,为了保证不重复,选举p1加入凸包点集结束divide。
三角形面积公式:
| x1 y1 1 |
| x2 y2 1 | = x1y2+y1x3+x2y3-y2x3-y1x2-x1y3
| x3 y3 1 |
== 三角形面积两倍 ;若结果为正,则p1p2p3为逆时针,即p3在p1,p2左侧。
此面积公式可用来找出距离p1p2线段最远的点和对点集分类。
用ax+by=c 也可用来判断点在直线的哪一侧:
a = p2.y - p1.y;
b = p1.x - p2.x;
c = p1.x * p2.y - p2.x * p1.y;
即 a=y2-y1, b=x1-x2, c=x1y2-x2y1;
POJ2187 AC的java代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
/**
*
* @author fzh
*/
public class Tubao {
static class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString(){
return "("+x+" "+y+")";
}
}
static class Line {
Point start;
Point end;
int a, b, c;
public Line(Point p1, Point p2) {
start = p1;
end = p2;
a = p2.y - p1.y;
b = p1.x - p2.x;
c = p1.x * p2.y - p2.x * p1.y;
}
public int result(Point p) { //求Point在Line的哪一侧
int k = a * p.x + b * p.y;
if (k > c) {
return 1;
}
if (k < c) {
return -1;
} else {
return 0;
}
}
}
static ArrayList<Point> resu = new ArrayList();
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
ArrayList<Point> set = new ArrayList();
for (int i = 0; i < num; i++) {
int x = scan.nextInt();
int y = scan.nextInt();
set.add(new Point(x, y));
}
Collections.sort(set, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Point p1 = (Point) o1;
Point p2 = (Point) o2;
return p1.x > p2.x ? 1 : -1;
}
});
Point p1 = set.get(0);
Point p2 = set.get(set.size()-1);
Line line = new Line(p1, p2);
ArrayList<Point> pleft = new ArrayList();
ArrayList<Point> pright = new ArrayList();
for (int i = 0; i < set.size(); i++) {
Point p3 = set.get(i);
int b = getArea(p1, p2, p3);
if (b > 0) {
pleft.add(p3);
} else if (b < 0) {
pright.add(p3);
}
}
divide(p1,p2, pleft);
divide(p2,p1, pright);
int maxl=0;
for(int i=0;i<resu.size()-1;i++){
for(int j=i+1;j<resu.size();j++){
Point pp1=resu.get(i);
Point pp2=resu.get(j);
int len=(pp1.x-pp2.x)*(pp1.x-pp2.x)+(pp1.y-pp2.y)*(pp1.y-pp2.y);
if(len>maxl) maxl=len;
}
}
System.out.println(maxl);
}
public static int getArea(Point p1, Point p2, Point p3) { //大于0为逆时针,p3在p1 p2左边,其绝对值为三角形面积两倍
return p1.x * p2.y + p1.y * p3.x + p2.x * p3.y - p2.y * p3.x - p1.y * p2.x - p1.x * p3.y;
}
public static void divide(Point p1,Point p2, ArrayList<Point> set) { //set集合都在p1,p2左边
if(set.isEmpty()){
resu.add(p1);
return;
}
int max=0;
Point top=null;
for (int i = 0; i < set.size(); i++) {
Point p3 = set.get(i);
int b = getArea(p1, p2, p3);
if(max<b){
max=b;
top=p3;
}
}
ArrayList<Point> s1 = new ArrayList();
ArrayList<Point> s2 = new ArrayList();
for(int i=0;i<set.size();i++){
Point p3=set.get(i);
if(getArea(p1,top,p3)>0){
s1.add(p3);
}else if(getArea(top,p2,p3)>0){
s2.add(p3);
}
}
divide(p1,top,s1);
divide(top,p2,s2);
}
}