알고리즘/백준
[백준] 13310번 - 먼 별
래울
2025. 2. 9. 04:16
문제
https://www.acmicpc.net/problem/13310
풀이
캘리퍼스 + 삼분 탐색
각 시간마다 찍힌 별들 중 최대 거리에 대해 최소값을 찾는 문제
f(x)에 대한 삼분 탐색이지만 f(x)가 캘리퍼스 구현 ㅇㅇ..., distance 반환값을 int로 해놓고 발견못해서 고통받았다.
+ 계속 틀리길래 혹시해서 탐색 구현을 아래처럼 변경했더니 pass됨, 왜 그런지는 좀 생각해봐야 할듯
// 틀림
if (sim(lm) < sim(rm)) {
right = rm;
}
else {
left = lm;
}
// Pass
if (sim(lm) > sim(rm)) {
left = lm;
}
else {
right = rm;
}
코드
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
struct Input {
long long x, y, dx, dy;
};
vector<Input> stars;
struct Dot {
long long x, y;
};
int N, T;
vector<Dot> input;
stack<Dot> s;
vector<Dot> convexHull;
int cross(Dot& a, Dot& b, Dot& c)
{
Dot line1 = { b.x - a.x, b.y - a.y };
Dot line2 = { c.x - b.x, c.y - b.y };
long long r = line1.x * line2.y - line1.y * line2.x;
if (r < 0) return -1;
if (r > 0) return 1;
return 0;
}
int cross2(Dot& a, Dot& b, Dot& c, Dot& d) {
Dot line1 = { b.x - a.x, b.y - a.y };
Dot line2 = { d.x - c.x, d.y - c.y };
long long r = line1.x * line2.y - line1.y * line2.x;
if (r < 0) return -1;
if (r > 0) return 1;
return 0;
}
long long getDistance(Dot a)
{
return (input[0].x - a.x) * (input[0].x - a.x) + (input[0].y - a.y) * (input[0].y - a.y);
}
bool cmp(Dot& a, Dot& b) {
int r = cross(input[0], a, b);
if (r > 0) return 1;
if (r == 0) return getDistance(a) < getDistance(b);
return 0;
}
long long sim(int day)
{
input.clear();
convexHull.clear();
// 현재 시간에 별들의 위치 정보 계산
for (int i = 0; i < stars.size(); ++i) {
input.push_back({
stars[i].x + stars[i].dx * day,
stars[i].y + stars[i].dy * day,
});
}
// convex hull 계산
Dot temp;
for (int i = 1; i < input.size(); ++i) {
if (input[i].x < input[0].x) {
temp = input[0];
input[0] = input[i];
input[i] = temp;
}
else if (input[i].x == input[0].x && input[i].y < input[0].y) {
temp = input[0];
input[0] = input[i];
input[i] = temp;
}
}
sort(input.begin() + 1, input.end(), cmp);
s.push(input[0]);
s.push(input[1]);
for (int i = 2; i < input.size(); ++i)
{
while (s.size() >= 2) {
Dot prev = s.top();
s.pop();
Dot prev2 = s.top();
if (cross(prev2, prev, input[i]) > 0) {
s.push(prev);
break;
}
}
s.push(input[i]);
}
// 캘리퍼스 계산
while (!s.empty()) {
//cout << s.top().x << ", " << s.top().y << "\n";
convexHull.push_back(s.top());
s.pop();
}
int index = 1;
long long maxDis = 0;
int cs = convexHull.size();
for (int i = 0; i < cs; ++i) {
while (cross2(convexHull[i], convexHull[(i + 1) % cs], convexHull[index % cs], convexHull[(index + 1) % cs]) < 0) {
long long currDis = (convexHull[i].x - convexHull[index % cs].x) * (convexHull[i].x - convexHull[index % cs].x)
+ (convexHull[i].y - convexHull[index % cs].y) * (convexHull[i].y - convexHull[index % cs].y);
if (maxDis < currDis) {
maxDis = currDis;
}
index++;
}
long long currDis = (convexHull[i].x - convexHull[index % cs].x) * (convexHull[i].x - convexHull[index % cs].x)
+ (convexHull[i].y - convexHull[index % cs].y) * (convexHull[i].y - convexHull[index % cs].y);
if (maxDis < currDis) {
maxDis = currDis;
}
}
//maxDis.
//cout << "maxDis: " << maxDis << "\n";
return maxDis;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
cin >> N >> T;
int x, y, dx, dy;
for (int i = 0; i < N; ++i) {
cin >> x >> y >> dx >> dy;
stars.push_back({ x, y, dx, dy });
}
long long shortestDistance = 8000000000000000000;
int shortestDay = 0;
if (N == 2) {
for (int i = 0; i <= T; ++i) {
Dot star1 = { stars[0].x + stars[0].dx * i, stars[0].y + stars[0].dy * i };
Dot star2 = { stars[1].x + stars[1].dx * i, stars[1].y + stars[1].dy * i };
long long currDis = (star1.x - star2.x) * (star1.x - star2.x)
+ (star1.y - star2.y) * (star1.y - star2.y);
//cout << currDis << "\n";
if (currDis < shortestDistance) {
shortestDistance = currDis;
shortestDay = i;
}
}
cout << shortestDay << "\n" << shortestDistance;
}
else {
if (T <= 20) {
for (int i = 0; i <= T; ++i) {
long long ret = sim(i);
if (ret < shortestDistance) {
shortestDistance = ret;
shortestDay = i;
}
}
cout << shortestDay << "\n" << shortestDistance;
}
else {
//삼분
int left = 0;
int right = T;
while (right - left >= 3) {
int lm = (left * 2 + right) / 3;
int rm = (right * 2 + left) / 3;
if (sim(lm) > sim(rm)) {
left = lm;
}
else{
right = rm;
}
}
for (int i = left; i < left + 3; ++i) {
long long ret = sim(i);
//cout << i << ": " << ret << "\n";
if (ret < shortestDistance) {
shortestDistance = ret;
shortestDay = i;
}
}
cout << shortestDay << "\n" << shortestDistance;
}
}
return 0;
}