public classConvexHullExhaustive { public static classLine {//线 Point p1, p2; Line(Point p1, Point p2) { this.p1 = p1; this.p2 = p2; } }
public static classPoint{//点 int x; int y; }
List<Point> pts = null;//点集 List<Line> lines = new ArrayList<Line>();//点集pts的凸包
public void setPointList(List<Point> pts) { this.pts = pts; }
public List<Line> eval() { lines.clear(); if (pts == null) { return lines; } int n = pts.size(); int a, b, c, cc, l, r; for (int i = 0; i < n; i++) { for (int j = i+1; j < n; j++) {//穷举点集中任意两点组成的直线ax+by=c a = pts.get(j).y - pts.get(i).y;// b = pts.get(i).x - pts.get(j).x; c = pts.get(i).x * pts.get(j).y -pts.get(i).y * pts.get(j).x; l = r = 0; int k; for (k = 0; k < n; k++) {//穷举点集中的每一点 cc = a * pts.get(k).x + b * pts.get(k).y; if (cc > c) l++; elseif (cc < c) r++; if (l * r != 0) break;//直线两侧都有点, } if (k == n) {//凸包中添加一条边 lines.add(new Line(pts.get(i), pts.get(j))); } } } return lines; } }
import java.util.Scanner; public classConvexHullWithGraham { Point[] ch; //点集p的凸包 Point[] p ; //给出的点集 int n; int l; int len=0; public ConvexHullWithGraham(Point[] p,int n,int l){ this.p=p; this.n=n; this.l=l; ch= new Point[n]; } //小于0,说明向量p0p1的极角大于p0p2的极角 public double multiply(Point p1, Point p2, Point p0) { return ((p1.getX() - p0.getX()) * (p2.getY() - p0.getY()) - (p2.getX()- p0.getX()) * (p1.getY()- p0.getY())); } //求距离 public double distance(Point p1, Point p2) { return (Math.sqrt((p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) + (p1.getY() - p2.getY()) * (p1.getY() - p2.getY()))); } public void answer(){ double sum = 0; for (int i = 0; i < len - 1; i++) { sum += distance(ch[i], ch[i + 1]); } if (len > 1) { sum += distance(ch[len - 1], ch[0]); } sum += 2 * l * Math.PI; System.out.println(Math.round(sum)); } public int Graham_scan() { int k = 0, top = 2; Point tmp; //找到最下且偏左的那个点 for (int i = 1; i < n; i++) if ((p[i].getY() < p[k].getY()) || ((p[i].getY() == p[k].getY()) && (p[i].getX() < p[k].getX()))) k = i; //将这个点指定为pts[0],交换pts[0]与pts[k] tmp = p[0]; p[0] = p[k]; p[k] = tmp; //按极角从小到大,距离偏短进行排序 for (int i = 1; i < n - 1; i++) { k = i; for (int j = i + 1; j < n; j++) if ((multiply(p[j], p[k], p[0]) > 0) || ((multiply(p[j], p[k], p[0]) == 0) && (distance( p[0], p[j]) < distance( p[0], p[k])))) k = j; //k保存极角最小的那个点,或者相同距离原点最近 tmp = p[i]; p[i] = p[k]; p[k] = tmp; } //前三个点先入栈 ch[0] = p[0]; ch[1] = p[1]; ch[2] = p[2]; //判断与其余所有点的关系 for (int i = 3; i < n; i++) { //不满足向左转的关系,栈顶元素出栈 while (top > 0 && multiply(p[i], ch[top], ch[top - 1]) >= 0) top--; //当前点与栈内所有点满足向左关系,因此入栈. ch[++top] = p[i]; } len=top+1; return len; } /** 样例: Sample Input 9100 200400 300400 300300 400300 400400 500400 500200 350200 200200
Sample Output 1628 */ public static void main(String[] args) { Scanner in=new Scanner(System.in); int n = in.nextInt(); int l = in.nextInt(); int x, y; Point[] p = new Point[n]; for (int i = 0; i < n; i++) { x = in.nextInt(); y = in.nextInt(); p[i] = new Point(x, y); } ConvexHullWithGraham ma=new ConvexHullWithGraham(p,n,l); ma.Graham_scan(); ma.answer(); } }
public classConvexHullWithMelkman { private Point[] pointArray;//坐标数组 private final int N;//数据个数 private int D[]; // 数组索引,双向表
public ConvexHullWithMelkman(List<Point> pList) { this.pointArray = new Point[pList.size()]; N = pList.size(); int k = 0; for (Point p : pList) { pointArray[k++] = p; } D = new int[2 * N]; }
/** * 求凸包点 * * @return 所求凸包点 */ public Point[] getTubaoPoint() { // 获得最小的Y,作为P0点 float minY = pointArray[0].getY(); int j = 0; for (int i = 1; i < N; i++) { if (pointArray[i].getY() < minY) { minY = pointArray[i].getY(); j = i; } } //交换内容 swap(0, j); // 计算除第一顶点外的其余顶点到第一点的线段与x轴的夹角 for (int i = 1; i < N; i++) { pointArray[i].setArCos(angle(i)); } quickSort(1, N - 1); // 根据所得到的角度进行快速排序 int bot = N - 1; int top = N; D[top++] = 0; D[top++] = 1; int i; for (i = 2; i < N; i++) {// 寻找第三个点 要保证3个点不共线!!==0 代表共线 if (isLeft(pointArray[D[top - 2]], pointArray[D[top - 1]], pointArray[i]) != 0) break; D[top - 1] = i; // 共线就更换顶点 } D[bot--] = i; D[top++] = i; // i是第三个点 不共线!! // 最初的一次 int t; if (isLeft(pointArray[D[N]], pointArray[D[N + 1]], pointArray[D[N + 2]]) < 0) { // 此时队列中有3个点,要保证3个点a,b,c是成逆时针的,不是就调换ab t = D[N]; D[N] = D[N + 1]; D[N + 1] = t; } //开始 for (i++; i < N; i++) { // 如果成立就是i在凸包内,跳过 //top=n+3 bot=n-2 // 此时top、bot没有改变 if (isLeft(pointArray[D[top - 2]], pointArray[D[top - 1]],pointArray[i]) > 0 && isLeft(pointArray[D[bot + 1]], pointArray[D[bot + 2]],pointArray[i]) > 0) { continue; } //非左转 则退栈 while (isLeft(pointArray[D[top - 2]], pointArray[D[top - 1]], pointArray[i]) <= 0) { top--; } D[top++] = i; //反向表非左转 则退栈 while (isLeft(pointArray[D[bot + 1]], pointArray[D[bot + 2]], pointArray[i]) <= 0) { bot++; } D[bot--] = i; } // 凸包构造完成,D数组里bot+1至top-1内就是凸包的序列(头尾是同一点) Point[] resultPoints = new Point[top - bot - 1]; int index = 0; for (i = bot + 1; i < top - 1; i++) { System.out.println(pointArray[D[i]].getX() + "," + pointArray[D[i]].getY()); resultPoints[index++] = pointArray[D[i]]; } return resultPoints; } /** * 判断ba相对ao是不是左转 * * @return 大于0则左转 */ private float isLeft(Point o, Point a, Point b) { float aoX = a.getX() - o.getX(); float aoY = a.getY() - o.getY(); float baX = b.getX() - a.getX(); float baY = b.getY() - a.getY(); return aoX * baY - aoY * baX; } /** * 实现数组交换 * * @param i * @param j */ private void swap(int i, int j) { Point tempPoint = new Point(); tempPoint.setX(pointArray[j].getX()); tempPoint.setY(pointArray[j].getY()); tempPoint.setArCos(pointArray[j].getArCos()); pointArray[j].setX(pointArray[i].getX()); pointArray[j].setY(pointArray[i].getY()); pointArray[j].setArCos(pointArray[i].getArCos()); pointArray[i].setX(tempPoint.getX()); pointArray[i].setY(tempPoint.getY()); pointArray[i].setArCos(tempPoint.getArCos()); } /** * 快速排序 * * @param top * @param bot */ private void quickSort(int top, int bot) { int pos; if (top < bot) { pos = loc(top, bot); quickSort(top, pos - 1); quickSort(pos + 1, bot); } } /** * 移动起点,左侧为小,右侧为大 * @param top * @param bot * @return 移动后的位置 */ private int loc(int top, int bot) { double x = pointArray[top].getArCos(); int j, k; j = top + 1; k = bot; while (true) { // while (j < bot && pointArray[j].getArCos() < x) j++; // while (k > top && pointArray[k].getArCos() > x) k--; if (j >= k) break; swap(j, k); } swap(top, k); return k; } /** * 角度计算 * * @param i 指针 * @return */ private double angle(int i) { double j, k, m, h; j = pointArray[i].getX() - pointArray[0].getX(); k = pointArray[i].getY() - pointArray[0].getY(); m = Math.sqrt(j * j + k * k); // 得到顶点i 到第一顶点的线段长度 if (k < 0) j = (-1) * Math.abs(j); h = Math.acos(j / m); // 得到该线段与x轴的角度 return h; }
/**data.txt: 01 04 13 22 31 34 */ public static void main(String args[]) { // File file = new File("G:/yl.txt"); File file = new File("D:/data.txt"); BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); } catch (FileNotFoundException e) { e.printStackTrace(); } List<Point> pointList = new ArrayList<Point>(); String str = null; try { str = br.readLine(); } catch (IOException e) { e.printStackTrace(); } while (str != null) { String[] s = str.split("\\t", 2); float x = Float.parseFloat(s[0].trim()); float y = Float.parseFloat(s[1].trim()); Point p = new Point(); p.setX(x); p.setY(y); // System.out.println("文件数据:" + x + ", " + y); pointList.add(p); try { str = br.readLine(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("数据个数:" + pointList.size()); ConvexHullWithMelkman m = new ConvexHullWithMelkman(pointList); m.getTubaoPoint(); } }