問題を解く-最近2時(JAVA)
34626 ワード
最近の2時
に答える
ベースラインの左側から2点までの距離の最大値 ベースラインの右側から2点までの距離の最大値 2点間距離ベースラインの最大値 連結プロセスは、3つの分割問題の答えの最小値を返します.しかし、問題があります.3つ目の問題を解決する方法を考えなければなりません.全左点と全右点をベースラインに対して比較した場合,時間的複雑度はO(n 2)O(n^2)O(n 2)であるため求められない.ここで洞察をしなければならない.ベースライン間の2点、すなわち1番目の質問の答えと2番目の質問の答えの最大値minDistanceDistanceDistanceDistanceDistance間の距離を比較する必要はありません.もっと遠く離れていると、最初の問題と2番目の問題よりも答えが大きいからです.私たちはもっと大きな価格を考える必要はありません.最高価格を求める必要があるからです.
ここまで考えると、x座標が同じでy座標だけが異なる点が多い場合、効率が低いことがわかります.この問題を解決するには、y座標で中間の点をソートし、一番下の点から別の一番上の点までの距離を順番に比較します.y座標との差が「最小距離」より大きい点を順番に比較する場合は、その点は比較を停止し、他の点を比較する必要があります.一部の点では、1つの点の最大最小距離から1つの点までの距離を求める必要があるため、時間的複雑度はO(n)O(n)O(n)O(n)O(n)である.
3番目のコンポーネントを特定する必要があります.分割領域内の点の個数が数以下である場合、分割すべきでないか否かを決定しなければならない.2個未満で分割を放棄して2点間の距離を返すと、3個残っている場合に1個と2個に分割される場合があります.分割領域の点数が1の場合、距離を求めることができないため、3点が残っている場合は、3点間のすべての距離を比較して最大値を返す必要があります.
じかんふくごうどぶんせき
に答える
無知に解けますか?
すべての点間の距離を求めて比較することで,解くことができる.しかし,時間的複雑度はO(n 2)O(n^2)O(n 2)であり,絶対時間では解決できない.
分割征服アルゴリズム
分割征服アルゴリズムを使用して問題を解決するには、3つのコンポーネントを作成する必要があります.
最初のコンポーネントを作成するには、何に基づいて問題を区別する必要があります.中心点のx座標で分割する方法を簡単に思い出すことができます.ただし、このように分割すると、2つの点間のベースライン距離は比較されません.そのため、3つの問題を解決する必要があります.
ここまで考えると、x座標が同じでy座標だけが異なる点が多い場合、効率が低いことがわかります.この問題を解決するには、y座標で中間の点をソートし、一番下の点から別の一番上の点までの距離を順番に比較します.y座標との差が「最小距離」より大きい点を順番に比較する場合は、その点は比較を停止し、他の点を比較する必要があります.一部の点では、1つの点の最大最小距離から1つの点までの距離を求める必要があるため、時間的複雑度はO(n)O(n)O(n)O(n)O(n)である.
3番目のコンポーネントを特定する必要があります.分割領域内の点の個数が数以下である場合、分割すべきでないか否かを決定しなければならない.2個未満で分割を放棄して2点間の距離を返すと、3個残っている場合に1個と2個に分割される場合があります.分割領域の点数が1の場合、距離を求めることができないため、3点が残っている場合は、3点間のすべての距離を比較して最大値を返す必要があります.
じかんふくごうどぶんせき
1回の呼び出しは関数を半分に分割し、時間的複雑度O(n)O(n)O(n)O(n)の関数を呼び出して上記3番目の問題を解決するので、T(n)=2 T(n 2)+nT(n)=2 T({n\over 2})+nT(n)=2 T(n)+n)となる.主定理によれば,我々が実施したアルゴリズムの時間的複雑さはO(nloglogn)O(nlogn)O(nlogn)である.
インプリメンテーション import java.util.*;
class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getDistance(Point point) {
return (this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y);
}
}
public class Main {
// 모든 점의 개수
public static int n;
// 모든 점
public static Point[] points;
// 답
public static int result;
public static void input() {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
points = new Point[n];
for (int i = 0; i < n; i++) {
int x = scanner.nextInt();
int y = scanner.nextInt();
points[i] = new Point(x, y);
}
Arrays.sort(points, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return o1.x - o2.x;
}
});
}
public static void solve() {
result = getMinDistance(0, n - 1);
}
// 두 점들 간의 거리의 최솟값을 반환합니다.
public static int getMinDistance(int left, int right) {
// 기저 사례: 점들의 개수가 3개 이하면 브루트 포스로 계산
if (right - left + 1 <= 3) {
return getMinDistanceByBruteForce(left, right);
}
// 가운데 점의 x좌표 기준으로 문제 분할
int mid = (right + left) / 2;
int distance = Math.min(getMinDistance(left, mid), getMinDistance(mid + 1, right));
// 가운데 영역 안의 문제 해결
int middleLeft = left;
int middleRight = right;
int temp = points[mid].x - points[middleLeft].x;
while (temp * temp > distance) {
middleLeft++;
temp = points[mid].x - points[middleLeft].x;
}
temp = points[middleRight].x - points[mid].x;
while (temp * temp > distance) {
middleRight--;
temp = points[middleRight].x - points[mid].x;
}
distance = Math.min(distance, getMinDistanceInMiddle(distance, middleLeft, middleRight));
return distance;
}
// 가운데 영역 안의 두 점들 간의 거리의 최솟값 계산
public static int getMinDistanceInMiddle(int minDistance, int left, int right) {
int distance = minDistance;
// 기저 사례: 점들의 개수가 3개 이하면 브루트 포스로 계산
int size = right - left + 1;
if (size <= 3) {
return getMinDistanceByBruteForce(left, right);
}
// 가운데 영역 안의 점들은 y좌표 기준으로 정렬함
Point[] midPoints = new Point[size];
for (int i = 0; i < size; i++) {
midPoints[i] = points[left + i];
}
Arrays.sort(midPoints, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return o1.y - o2.y;
}
});
// 2중 반복문이라서 시간 복잡도가 O(n^2)이라고 생각할 수 있지만 조건문 때문에 안쪽의 반복문은 최대 minDistance번 실행됨
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
int temp = midPoints[j].y - midPoints[i].y;
if (temp * temp >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
return distance;
}
// 브루트 포스로 영역 안의 두 점들 간의 거리의 최솟값 계산
public static int getMinDistanceByBruteForce(int left, int right) {
int distance = Integer.MAX_VALUE;
for (int i = left; i <= right - 1; i++) {
for (int j = i + 1; j <= right; j++) {
distance = Math.min(distance, points[i].getDistance(points[j]));
}
}
return distance;
}
public static void output() {
System.out.println(result);
}
public static void main(String[] args) {
input();
solve();
output();
}
}
振り返る
実装前はアルゴリズムに問題はなかったと思いますが、タイムアウトが続いていたので、コードを逐一分析し、原因を特定するために様々な仮定を確立しました.
1.Scannerを使ってデータを受信する時間が長すぎますか?
スキャンプログラムを入力すると、BufferedReaderを入力するとタイムアウトが表示されます.問題解決後、BufferedReaderを使用するとScannerを使用する場合との時間差は約1.3倍になりますが、これはScannerの問題ではありません.
2.街をぶらつくときMath.pow()関数はもっと時間がかかりますか?
浮動小数点の乗算は整数の乗算よりも長いが、これは問題ではない.
3.変数を保存していませんが、もう一度計算するのに時間がかかりますか? for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if ((midPoints[j].y - midPoints[i].y) * (midPoints[j].y - midPoints[i].y) >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
中間領域の点の距離を比較するための繰り返し文では、変数に格納するのではなく、比較するたびに2つの点のy座標の違いを計算しますか?と思ったけど. for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
int temp = (midPoints[j].y - midPoints[i].y);
if (temp * temp >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
こうなっても結果は変わらない
4.並び順が遅いですか?それをリストに並べて並べ替えますか?
念のためCollectionssort()関数の実装コードを表示し、配列リストを配列に設定し、Arraysを設定します.配列をsort()にソートし、配列リストに再リストして返します.したがって、Collections.sort()関数はArraysです.sort()より速くはあり得ない.
本当の原因はgetMinDistance()関数でint middleLeft=0である.なぜなら.中間領域を定義する場合、関数を呼び出す際には入力パラメータの左側から考慮し、0から考慮する必要があるため、関数を呼び出すたびにパラメータに関係なく時間複雑度O(n)O(n)O(n)O(n)O(n)の操作が実行される.
コードを作るときはこのような小さなミスがたくさんあります.もっと集中する必要があります.
Reference
この問題について(問題を解く-最近2時(JAVA)), 我々は、より多くの情報をここで見つけました
https://velog.io/@qwe910205/문제-풀이-가장-가까운-두-점JAVA
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
import java.util.*;
class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getDistance(Point point) {
return (this.x - point.x) * (this.x - point.x) + (this.y - point.y) * (this.y - point.y);
}
}
public class Main {
// 모든 점의 개수
public static int n;
// 모든 점
public static Point[] points;
// 답
public static int result;
public static void input() {
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
points = new Point[n];
for (int i = 0; i < n; i++) {
int x = scanner.nextInt();
int y = scanner.nextInt();
points[i] = new Point(x, y);
}
Arrays.sort(points, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return o1.x - o2.x;
}
});
}
public static void solve() {
result = getMinDistance(0, n - 1);
}
// 두 점들 간의 거리의 최솟값을 반환합니다.
public static int getMinDistance(int left, int right) {
// 기저 사례: 점들의 개수가 3개 이하면 브루트 포스로 계산
if (right - left + 1 <= 3) {
return getMinDistanceByBruteForce(left, right);
}
// 가운데 점의 x좌표 기준으로 문제 분할
int mid = (right + left) / 2;
int distance = Math.min(getMinDistance(left, mid), getMinDistance(mid + 1, right));
// 가운데 영역 안의 문제 해결
int middleLeft = left;
int middleRight = right;
int temp = points[mid].x - points[middleLeft].x;
while (temp * temp > distance) {
middleLeft++;
temp = points[mid].x - points[middleLeft].x;
}
temp = points[middleRight].x - points[mid].x;
while (temp * temp > distance) {
middleRight--;
temp = points[middleRight].x - points[mid].x;
}
distance = Math.min(distance, getMinDistanceInMiddle(distance, middleLeft, middleRight));
return distance;
}
// 가운데 영역 안의 두 점들 간의 거리의 최솟값 계산
public static int getMinDistanceInMiddle(int minDistance, int left, int right) {
int distance = minDistance;
// 기저 사례: 점들의 개수가 3개 이하면 브루트 포스로 계산
int size = right - left + 1;
if (size <= 3) {
return getMinDistanceByBruteForce(left, right);
}
// 가운데 영역 안의 점들은 y좌표 기준으로 정렬함
Point[] midPoints = new Point[size];
for (int i = 0; i < size; i++) {
midPoints[i] = points[left + i];
}
Arrays.sort(midPoints, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return o1.y - o2.y;
}
});
// 2중 반복문이라서 시간 복잡도가 O(n^2)이라고 생각할 수 있지만 조건문 때문에 안쪽의 반복문은 최대 minDistance번 실행됨
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
int temp = midPoints[j].y - midPoints[i].y;
if (temp * temp >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
return distance;
}
// 브루트 포스로 영역 안의 두 점들 간의 거리의 최솟값 계산
public static int getMinDistanceByBruteForce(int left, int right) {
int distance = Integer.MAX_VALUE;
for (int i = left; i <= right - 1; i++) {
for (int j = i + 1; j <= right; j++) {
distance = Math.min(distance, points[i].getDistance(points[j]));
}
}
return distance;
}
public static void output() {
System.out.println(result);
}
public static void main(String[] args) {
input();
solve();
output();
}
}
振り返る
実装前はアルゴリズムに問題はなかったと思いますが、タイムアウトが続いていたので、コードを逐一分析し、原因を特定するために様々な仮定を確立しました.
1.Scannerを使ってデータを受信する時間が長すぎますか?
スキャンプログラムを入力すると、BufferedReaderを入力するとタイムアウトが表示されます.問題解決後、BufferedReaderを使用するとScannerを使用する場合との時間差は約1.3倍になりますが、これはScannerの問題ではありません.
2.街をぶらつくときMath.pow()関数はもっと時間がかかりますか?
浮動小数点の乗算は整数の乗算よりも長いが、これは問題ではない.
3.変数を保存していませんが、もう一度計算するのに時間がかかりますか? for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if ((midPoints[j].y - midPoints[i].y) * (midPoints[j].y - midPoints[i].y) >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
中間領域の点の距離を比較するための繰り返し文では、変数に格納するのではなく、比較するたびに2つの点のy座標の違いを計算しますか?と思ったけど. for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
int temp = (midPoints[j].y - midPoints[i].y);
if (temp * temp >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
こうなっても結果は変わらない
4.並び順が遅いですか?それをリストに並べて並べ替えますか?
念のためCollectionssort()関数の実装コードを表示し、配列リストを配列に設定し、Arraysを設定します.配列をsort()にソートし、配列リストに再リストして返します.したがって、Collections.sort()関数はArraysです.sort()より速くはあり得ない.
本当の原因はgetMinDistance()関数でint middleLeft=0である.なぜなら.中間領域を定義する場合、関数を呼び出す際には入力パラメータの左側から考慮し、0から考慮する必要があるため、関数を呼び出すたびにパラメータに関係なく時間複雑度O(n)O(n)O(n)O(n)O(n)の操作が実行される.
コードを作るときはこのような小さなミスがたくさんあります.もっと集中する必要があります.
Reference
この問題について(問題を解く-最近2時(JAVA)), 我々は、より多くの情報をここで見つけました
https://velog.io/@qwe910205/문제-풀이-가장-가까운-두-점JAVA
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
if ((midPoints[j].y - midPoints[i].y) * (midPoints[j].y - midPoints[i].y) >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
int temp = (midPoints[j].y - midPoints[i].y);
if (temp * temp >= distance) {
break;
}
distance = Math.min(distance, midPoints[i].getDistance(midPoints[j]));
}
}
Reference
この問題について(問題を解く-最近2時(JAVA)), 我々は、より多くの情報をここで見つけました https://velog.io/@qwe910205/문제-풀이-가장-가까운-두-점JAVAテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol