석유 회사들은 효율적인 시추 방법을 찾기 위해 연구 중이다. 이 문제는 2차원 공간에서 석유층을 수평선분으로 모델링하고, 하나의 시추공을 통해 얻을 수 있는 최대 석유량을 계산하는 것이다.
5 100 180 20 30 60 30 70 110 40 10 40 50 0 80 70
200
import java.util.*;
public class Main {
static class Pos implements Comparable<Pos> {
long x, y, size;
int index;
Pos(long x, long y, int index, long size) {
this.x = x;
this.y = y;
this.size = size;
this.index = index;
}
// 기준점에 대해 벡터로 변환
Pos moveByPivot(Pos p) {
if (this.y < p.y) {
return new Pos(p.x - this.x, p.y - this.y, index, size);
}
return new Pos(this.x - p.x, this.y - p.y, index, size);
}
// CCW를 이용해 방향성 비교
long ccw(Pos p) {
return this.x * p.y - this.y * p.x;
}
@Override
public int compareTo(Pos p) {
long tmp = this.ccw(p);
if (tmp == 0) {
return Long.compare(p.size, this.size);
}
return Long.compare(tmp, 0);
}
}
static int n;
static Pos[] pos;
// 특정 기준점에서 최대 석유량 계산
static long makePivot(Pos pivot) {
List<Pos> ps = new ArrayList<>();
for (Pos p : pos) {
Pos mp = p.moveByPivot(pivot);
if (mp.y > 0) {
ps.add(mp);
}
}
Collections.sort(ps);
boolean[] met = new boolean[n];
// 석유층이 중복으로 더해지지 않도록 체크
for (Pos p : ps) {
if (met[p.index]) {
p.size *= -1; // 중복된 석유층은 빼준다.
} else {
met[p.index] = true;
}
}
Collections.sort(ps);
long ans = pivot.size, sum = pivot.size;
for (Pos p : ps) {
sum += p.size;
ans = Math.max(ans, sum);
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
pos = new Pos[n * 2];
for (int i = 0; i < n; i++) {
long x0 = sc.nextLong();
long x1 = sc.nextLong();
long y = sc.nextLong();
long size = Math.abs(x1 - x0);
pos[i * 2] = new Pos(x0, y, i, size); // 시작점
pos[i * 2 + 1] = new Pos(x1, y, i, size); // 끝점
}
long result = 0;
for (Pos p : pos) {
result = Math.max(result, makePivot(p));
}
System.out.println(result);
}
}
Pos 클래스
moveByPivot
: 기준점을 중심으로 좌표를 변환해 벡터로 변경. ccw
: 기울기를 계산해 방향성을 판별.makePivot 함수
메인 로직
이 문제는 기하학적 문제 해결과 그리디 전략이 결합된 문제였다.
CCW 알고리즘과 기울기 정렬을 적절히 활용해 최적의 시추 방향을 계산할 수 있었다.
시추공을 어디서 뚫을지 효율적으로 결정하는 것이 문제 해결의 핵심이었다.