分治法在求解“最近对”问题中的应用(JAVA)
分治法在求解“最近對”問題中的應用
最近對問題在蠻力法中有過講解,時間復雜度為O(n^2),下面將會采用分治法講解這類問題,時間復雜度會降到O(nlogn)
我們將笛卡爾平面上n>1個點構成的集合稱為P。若2<= n <= 3時,我們1可以通過蠻力法求解。但當n>3時,采用分治法或許是個更好的選擇。假設這些點是按照x軸、y軸升序排列的,可以找出點集在x軸方向上的中位數m,做一條垂直x軸的分割線,由此點將點集劃分為左右兩個大小為n/2的子集P1和P2,之后通過遞歸求解出在子集中的最近對距離d1,d2,最后找出d=max{d1,d2}。
但是!!!不巧的是,我們忽略了一個問題,如果距離最近的兩個點剛好分別在兩個子集中,那么d就不是所有點對的最小距離。我們需要在每次合并子問題結果時,要加以判斷是否存在這樣的點對。方法是:只考慮以分割線為對稱軸、寬度為2d的垂直帶中的的點,因為其他點對的距離都是大于d的。
這里給出一個優化,當我們在垂直帶中找到一個點p,只需要考慮p之后的5個點即可。
這是因為:如果我們在垂直帶中找到p-p'兩點的距離小于p,由于我們的序列時經過排序的,所以p'一定在p之后,且兩點在y軸上的距離一定是小于d的(根據勾股定理,兩點之間的距離如果小于d,那么x軸分量和y軸分量都是小于d的,反之,不可能存在這個點)。所以在幾何學上,p'的位置一定在下圖中的淡黃色矩形區域。而矩形區域內一般只能包含少量的候選點,這個數量最大為6(根據鴿巢定理)。圖中6個紅色點為極端的臨界情況。我們將d * 2d的矩形劃分為d/2 * 2d/3的6塊區域,如果超過6個點,假設為7,那么一定會出現某個小矩形中有兩個點,這兩個點的最大距離為圖中紅線距離5/6d <d,這和d的意義不符。
import java.util.Arrays; import java.util.Comparator; import java.util.Scanner;class Point {double x;double y;Point (double x, double y) {this.x = x;this.y = y;} } public class Main {static Point[] point;static Point[] minP = new Point[2];static Scanner in = new Scanner(System.in);public static void main(String[] args) {int n = in.nextInt();point = new Point[n]; // for (int i = 0; i < n; i++) { // int a = in.nextInt(); // int b = in.nextInt(); // point[i] = new Point(a, b); // }point[0] = new Point(1,3);point[1] = new Point(2,1);point[2] = new Point(3,5);point[3] = new Point(4,4);point[4] = new Point(5,2);Arrays.sort(point,0, n, new Comparator<Point>() {@Overridepublic int compare(Point o1, Point o2) {return (int) (o1.x - o2.x);}});System.out.println(point.length);double minD = closestPoint(0, point.length-1);for (int i = 0; i < 2; i++) {System.out.println(minP[i].x + "," + minP[i].y);}System.out.println(minD);}private static double closestPoint(int low, int high) {Point[] temp1 = new Point[2];Point[] temp2 = new Point[2];Point[] p = new Point[high - low + 1];double d, d1, d2, d3;int index = 0;if (high - low == 1) {minP[0] = new Point(point[low].x, point[low].y);minP[1] = new Point(point[high].x, point[high].y);return distance(point[low], point[high]);}if (high - low == 2) {d1 = distance(point[low], point[low+1]);d2 = distance(point[low+1], point[high]);d3 = distance(point[low], point[high]);if ((d1 <= d2) && (d1 <= d3)) {minP[0] = new Point(point[low].x, point[low].y);minP[1] = new Point(point[low+1].x, point[low+1].y);return d1;} else if (d2 <= d3) {minP[0] = new Point(point[low+1].x, point[low+1].y);minP[1] = new Point(point[high].x, point[high].y);return d2;} else {minP[0] = new Point(point[low].x, point[low].y);minP[1] = new Point(point[high].x, point[high].y);return d3;}}int mid = (low + high) / 2;d1 = closestPoint(low, mid);temp1[0] = minP[0];temp1[1] = minP[1];d2 = closestPoint(mid+ 1, high);temp2[0] = minP[0];temp2[1] = minP[1];if (d1 < d2) {d = d1;minP[0] = temp1[0];minP[1] = temp1[1];} else {d = d2;minP[0] = temp2[0];minP[1] = temp2[1];}for (int i = mid;i>=low && (point[mid].x - point[i].x) < d; i--) {p[index++] = point[i];}for (int i = mid+1;i<=high && (point[i].x - point[mid].x) < d; i++) {p[index++] = point[i];}Arrays.sort(p, 0, index, new Comparator<Point>() {@Overridepublic int compare(Point o1, Point o2) {return (int) (o1.y - o2.y);}});for (int i = 0; i < index-1; i++) {for (int j = i+1; j < index; j++) {if ((p[j].y - p[i].y) >= d) {break;} else {d3 = distance(p[i], p[j]);if (d3 < d) {minP[0] = new Point(p[i].x, p[i].y);minP[1] = new Point(p[j].x, p[j].y);}}}}return d;}private static double distance(Point p1, Point p2) {return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));} }
Input:
5
Output:
4.0,4.0
3.0,5.0
1.4142135623730951
總結
以上是生活随笔為你收集整理的分治法在求解“最近对”问题中的应用(JAVA)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: attrib指令
- 下一篇: ffmpeg转mp4格式