Loading [MathJax]/jax/output/HTML-CSS/jax.js
본문 바로가기

백준/기하

백준 2022 - 사다리

728x90

https://www.acmicpc.net/problem/2022

사전 지식

본인은 이 문제를 삼분 탐색으로 풀었다.

 

그 외에도 풀이를 이해하기 위해선 중학교 수준의 기하 지식과 비례식에 대한 이해가 필요하다.

풀이

구하려는 밑변의 길이를 z=a+b라고 두자. 그리고 x와 y는 다음과 같이 정리가 가능하다.

하지만 z의 길이를 모르는데 a, b를 구할 수 없다. 우리는 z를 구하는 대신 건물 벽에 접촉한 지점의 높이에 해당하는 옆변의 길이 wx, wy를 구할 것이다.

이렇게 wxwy를 꺼내면 wx와 c, 그리고 wy와 c는 각각 a:a+b=c:wy(b:a+b=c:wx)의 비례식으로 표현이 가능함을 발견할 수 있다. 그리고 wx, wy를 알아내면 피타고라스 정리를 통해 다음과 같은 수식을 전개할 수 있다.

 

x2=w2x+z2

y2=w2y+z2

>

z2=x2w2x

z2=y2w2y

>z=x2w2x=y2w2y

>x2w2x=y2w2y

>(x2w2x)(y2w2y)=0

 

그러면 이 문제는 wxwy를 구하는 문제로 바뀐다. 그러면 wxwy를 어떻게 구할 수 있을까? 이를 구하기 위해 우리는 위에 언급한 비례식에서 비율을 "임의로" 부여할 것이다. 임의로 비율을 부여하면 다음 관찰을 할 수 있다. 다음 설명은 c와 wy의 비율을 기준으로 한다.

c의 비율이 a일때 a보다 큰 비율을 부여하는 경우

a보다 과대평가된 상황이다. 과대평가한 비율로 계산한 wywy이라고 하고 wy와 크기 관계를 따져보면 wy<wy, 즉 원래 값보다 작게 측정된다. 반대로 wx를 기준으로 보면 비율이 과소평가되므로 wx>wx가 된다.

 

따라서 (x2w2x)(y2w2y)>0이다.

c의 비율이 a일때 a보다 작은 비율을 부여하는 경우

위의 경우의 완전히 반대다. wy>wy이고 wx<wx이므로 (x2w2x)(y2w2y)<0이다.

 

두 경우를 잘 생각하면 이 문제는 (x2w2x)(y2w2y)=0를 만족하는 비율 ratio를 구하는 문제가 된다.

 

함수 fratio=(x2w2x)(y2w2y)를 정의하고 개형을 보도록 하자.

정확하지 않은 개형이므로 참고에 유의한다.

여기서 0이 되는 지점을 어떻게 찾아야 할까? 함수를 fratio=|(x2w2x)(y2w2y)|로 재정의하고 개형을 보도록 하자.

 

정확하지 않은 개형이므로 참고에 유의한다.

그러면 극값이 0인 함수가 나오고 이런 유니모달 함수는 삼분 탐색으로 log 시간 복잡도에 극값을 찾을 수 있음이 알려져 있다.

 

따라서 우리는 삼분 탐색을 통해 fratio=0이 되는 ratio를 찾으면 된다. 0과 1 사이의 범위에서 함숫값이 0이 되도록 하는 ratio를 찾아 위에서 전개한 수식으로 z를 구하도록 하자.

유의 사항

삼분 탐색 과정에서 x2w2xy2w2y가 음수가 되는 경우가 있는데 sqrt() 함수에 음수를 넣으면 허수가 되어 값이 제대로 계산되지 않으니 0보다 작으면 0으로 바꿔줘야 한다.

전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,avx,avx2,fma")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
 
#include "bits/stdc++.h"
#include "ext/rope"
 
using namespace std;
using namespace __gnu_cxx;
 
using pii = pair<intint>;
using ll = long long;
 
double x, y, c;
 
double func(double ratio)
{
  double ywall = c / ratio;
  double xwall = c / (1 - ratio);
  double y_pred = sqrt(max(0., y * y - ywall * ywall));
  double x_pred = sqrt(max(0., x * x - xwall * xwall));
  return abs(y_pred - x_pred);
}
 
double guess(double ratio)
{
  double ywall = c / ratio;
  return sqrt(y * y - ywall * ywall);
}
 
void solve()
{
  cout.precision(10);
  cout << fixed;
 
  cin >> x >> y >> c;
 
  double l = 0., r = 1;
 
  for (int _ = 0; _ < 100; _++)
  {
    double diff = r - l;
    double p1 = l + diff / 3;
    double p2 = l + diff / 3 * 2;
 
    if (func(p1) > func(p2))
    {
      l = p1;
    }
    else
    {
      r = p2;
    }
  }
 
  cout << guess((l + r) / 2);
}
 
int main()
{
#ifdef LOCAL
  freopen("input.txt""r", stdin);
#endif
 
  cin.tie(nullptr);
  ios::sync_with_stdio(false);
 
  solve();
}
cs

제출 기록

728x90

'백준 > 기하' 카테고리의 다른 글

[ICPC] 백준 3861 - Slalom  (0) 2022.07.02
[ICPC] 백준 9015 - 정사각형  (0) 2022.06.08
백준 1064 - 평행사변형  (0) 2020.11.19
백준 14400 - 편의점 2  (0) 2020.06.26