# 팀원 구성

팀원은 cozyyg, kipa00, crescent_h입니다. kipa00과는 UCPC 5번째, 전체 PS 팀 대회로 따지면 8번째, 해시코드까지 합치면 12번째로 함께 나간 대회입니다. 사실 너무 많이 나가서 이 횟수가 맞는지도 잘 모르겠습니다... crescent_h와는 2019년 이후 2번째로 UCPC에 함께 나갔습니다.

팀명이 정해진 과정은 다음과 같습니다.

흔한 디자인 의뢰
이렇게 아무말을 하다 보면 팀명이 완성된다!
그러나 이 팀명이 스코어보드에 쓰이는 일은 없었다
그렇게 팀명을 정하면서 생겼던 고난과 역경을 모두 담은 팀명이 완성되었다

# 대회 준비

팀으로 준비한 건 딱히 없었습니다. 평소에 각자 풀고 싶은 문제를 자유롭게 풀어보면서 대비했습니다.

팀원들은 공통적으로 애드 혹 문제들을 잘 풉니다. 장점이긴 한데, 애드 혹 문제의 경우 실력이 충분하지 않다면 문제 하나에 모든 시간을 쏟아붓고도 풀지 못하는 대참사가 벌어질 수 있기 때문에 양날의 검이라고 생각합니다. 올해도 애드 혹 문제가 나왔는데, H번 문제는 어떻게 되었을까요?

더보기

cozyyg가 맛있게 먹었습니다.

# 대회 전략 수립

문제 분배를 제외하고는 크게 신경쓸 부분은 없었습니다.

cozyyg는 문제를 읽는 속도, 그리고 대략의 풀이를 생각해내는 속도가 빠릅니다. 그래서 거의 대부분의 UCPC에서는 A부터 시작하여 3개씩 뛰어넘으며(ADGJ) 읽었습니다. 어차피 어떤 문제를 누가 먼저 읽는지는 팀원들의 대회 경험이 많은 팀에서는 크게 상관이 없지만, 이 방법에는 한 가지 큰 문제가 있었습니다.

cozyyg가 UCPC A번 문제를 풀면 항상 한 번 틀린다.

그래도 다시 한 번 A를 풀어서, 이 징크스를 스스로 끊어낼까 하는 생각도 해봤지만, "아직은 그럴 준비가 안 되었다" 라고 생각했습니다. 그래서 올해는 G H I J K를 읽었습니다. 이 전략은 과연 잘 통했을까요?

더보기

실제로 저는 F H I를 풀었고, 팀은 A번을 틀리지 않고 맞았으니, 전략은 꽤나 성공적이었다고 할 수 있습니다.

# 대회 결과

8솔브, 18등으로 예선을 마무리했습니다! 대회 종료 후에 Open Contest가 열려서 스코어보드 공개가 늦어졌지만, 본선 진출에 큰 문제가 없으리라 짐작할 수 있었습니다. 그래서 가벼운 마음으로 링고에 가서 맛있는 치킨과 피자와 오렌지 주스를 즐겼습니다.

# 대회 타임라인

각자 집에서 디스코드로 현황을 공유하다 보면, "이거 제가 짭니다", "D AC"와 같이 타임라인을 짐작할 수 있는 로그가 많이 남습니다. 하지만 올해는 오프라인에서 모여서 대회를 치다 보니, 정확한 시각과 다른 팀원들의 현황은 잘 모릅니다. 그래서 이 글에서는 cozyyg의 타임라인에만 초점을 맞추려 합니다.

(0:00 ~ 0:08) 문제 읽기

문제를 G번부터 순서대로 읽었습니다. 문제를 읽으면서 했던 생각은 다음과 같습니다.

 

G: 일단 tree_set이나 priority_queue 같은 게 필요할 것 같은 문제고, 정말 어처구니없는 쉬운 풀이가 있는 게 아닌 이상, 빨리 풀 수 있는 문제는 절대 아니라고 생각했습니다. (그리고 그런 풀이가 있는지는 중간에 스코어보드를 확인하면 쉽게 판단이 가능하기 때문에, 꼭 빨리 잡을 필요는 없는 문제입니다.)

H: 맛 있 다 !

I: 이런 형태의 문제는 어려워봤자 priority_queue의 간단한 활용 정도니, 다른 쉬운 문제가 없다면 이것부터 풀기로 했습니다.

J: 총 다섯 개의 줄이 주어진다. 라는 문장 하나만으로도, 이 문제가 어려운 문제임을 예상할 수 있었습니다. 그래서 문제 내용만 이해하고 넘어갔습니다.

K: 문제 관상이 너무나도 parametric search였습니다. 그래도 I번보다는 구현에 시간이 걸릴 것 같았고, 다른 사람이 짜도록 하고 H를 짜야겠다 라고 생각했습니다.

 

이런 과정을 거친 결과, 제가 할 일은 I를 짠 다음, 다른 쉬운 문제가 남아있지 않다면, 즐겁게 H를 음미하기 였습니다. 이 때 AD는 다른 팀원들이 이미 풀었습니다.

(0:08 ~ 0:14) I 구현

I번 문제의 코딩에 바로 착수했습니다. 생각을 좀 하다 보니 \(a_i - K \cdot i\)\(a_i + K \cdot i\)가 중요해 보였고, 이걸 priority queue로 관리하면 되겠다고 생각했습니다. 그런데 어차피 최소만 중요하고 이 값이 \(i\)마다 고정되니까, 최솟값만 들고 있어도 충분하면 된다는 결론이 나왔습니다. 그래서 빠르게 코드를 짰고, 14분 2초째에 낸 코드가 AC를 받았습니다.

(0:14 ~ 0:16) 스코어보드 확인

다른 쉬운 문제가 있지는 않은지 확인해보기로 했습니다. A D I만 많이 풀렸고, 간간히 BK를 푼 팀이 있는 정도였습니다. K가 생각했던 대로 전형적인 문제일 것이라는 확신을 가질 수 있었고, 저는 저의 할 일을 하러 가기로 했습니다. 이 때 다른 팀원들은 풀이가 나온 C를 짜고, 일찍 풀린 문제들인 BK를 보고 있었던 것 같습니다.

(0:16 ~ 0:57) H 맛있게 먹기 구현

풀이는, 문제 읽기 과정에서 이미 나왔습니다. 작년에 출제했던 돌무더기 게임 1 문제와 밀접한 관련이 있었기 때문에, 문제를 읽자마자 불가능한 경우를 바로 알아낼 수 있었습니다. 그리고 변환이 가역적이라는 점에 착안하여 두 팔찌를 작은 base case로 변환하면 되겠다고 생각했고, 몇몇 경우들을 메모장에 써보고 나니 구현할 준비가 완료되었습니다.

더보기

\(RG \rightarrow B\)

\(RRG \rightarrow RB \rightarrow G\)

\(RRR \rightarrow RBGR \rightarrow GGR \rightarrow GB \rightarrow R\)

\(GG \rightarrow RBG \rightarrow RR\)

이런 문제는 Python으로 짜고 싶을 때가 많습니다. 구현을 하다 보면 비슷한 과정을 반복하게 될 때가 많아서, 함수로 잘 분리해줘야 합니다. C++보다 Python이 나을 거라 생각해서 Python으로 구현을 시작했습니다. 여러 함수를 선언해가며 구현을 해나갔고, 한두 번의 디버깅 끝에 예제에 대해 정확한 출력이 나오는 것을 눈으로 확인했습니다. 57분째에 코드를 제출했더니 바로 맞았습니다. 제 코드가 어떻게 만들어졌는지, 자세한 구현 과정을 아래에 써둡니다.

더보기

1. 일단 base case를 정합니다. 저는 R, G, B, RR로 정했습니다.

2. 함수를 하나 만듭니다. 이 함수는, 문자열을 입력으로 받아서, 어떤 base case로 환원되는지, 그리고 환원하는 과정을 순서대로 return 하는 함수입니다. 일단 이런 함수를 만들 것이다 라는 사실만 알아두고, 이 함수의 구현은 나중에 진행합니다.

3. 출력 부분을 먼저 만들어줍니다. 환원되는 base case가 다르면 불가능하니까 -1을 출력하고 끝냅니다. 아직 a, b라는 리스트에 뭘 넣을지는 모르겠지만, 일단 출력 형태를 그대로 넣는다고 생각하고 함수를 짭니다. b에 있는 원소는 순서도 뒤집고, 각 쿼리도 뒤집어야 하니까, 쿼리를 뒤집는 함수도 하나 선언해줍니다. 여기까지를 구현하면 다음과 같습니다.

# 1 1 2
# 2 2 R G

def iv(x):
    pass

def f(s):
    pass

_,s=input().split()
_,t=input().split()
q, a = f(s)
r, b = f(t)
if q != r:
    print("-1\n")
    exit(0)
print(str(len(a)+len(b))+"\n")
for x in a:
    print(' '.join(map(str, x)))
for x in b[::-1]:
    print(' '.join(map(str, iv(x))))

4. 이렇게 구현해놓고 iv 함수를 짜려는데, 짤 수가 없습니다. 2 쿼리를 1 쿼리로 바꿀 수는 있는데, 1 쿼리를 2 쿼리로 바꾸려고 보니 색깔을 모릅니다. 그래서 a, b 리스트에 들어갈 형태를 수정해줍니다. 두 쿼리 모두, [쿼리 번호, 왼쪽 구슬의 인덱스, 왼쪽 구슬의 색깔, 오른쪽 구슬의 인덱스, 오른쪽 구슬의 색깔]을 저장하도록 합니다. 이때 원소를 출력하는 것이 조금 복잡해졌기 때문에, show라는 함수를 선언하여 수정하고, 주석도 수정해줍니다. 이제 iv 함수를 구현하려는데, 놀랍게도 쿼리 번호만 바꾸면 됩니다! 이럴 거면 왜 함수로 뺐지 라는 생각이 들 수도 있지만, 한 줄짜리 함수도 큰 의미가 있는 경우가 많습니다. 그대로 둡니다. 참고로 저는 다른 문제를 풀던 중 x, y를 입력으로 받아서 x*y를 return하는 함수도 그대로 둔 적 있습니다. 그 결과 코드 부분마다 실제 곱셈과 해당 프로세스 중 어떤 것을 시행한 건지 구분할 수 있어서 도움이 되었습니다.

import sys
input=lambda:sys.stdin.readline().strip()
print=sys.stdout.write

# 1 1 R 2 G
# 2 2 R 3 G

def show(p):
    if p[0] == 1:
        return f"1 {p[1]+1} {p[3]+1}\n"
    else:
        return f"2 {p[1]+1} {p[2]} {p[4]}\n"

def iv(x):
    return [3-x[0]] + x[1:]

def f(s):
    pass

_,s=input().split()
_,t=input().split()
q, a = f(s)
r, b = f(t)
if q != r:
    print("-1\n")
    exit(0)
print(str(len(a)+len(b))+"\n")
for p in a:
    print(show(p))
for p in b[::-1]:
    print(show(iv(p))

놀랍게도 여기서 f 함수만 구현하면 정답 코드가 완성됩니다. 문제 풀이의 핵심인 이 함수는 여러분이 직접 구현해보세요! 팁을 드리자면, 끝의 2개 또는 3개를 1개로 바꾸는 과정을 반복하면 됩니다. pop과 append는 \(O(1)\)에 되니까 시간 초과를 걱정할 필요가 없습니다.

그 사이 다른 팀원들은 C를 풀었고, K도 조금 뒤에 풀렸습니다.

(0:57 ~ 1:38) F 구현

B가 많이 풀리고 있길래, Bkipa00이 봤고, 풀이가 생각나긴 했는데 이렇게 어려운 게 어떻게 많이 풀리는 것인지 모르겠다고 했습니다. 그리고 F가 많이 풀리고 있었기 때문에, 귀찮은 자료구조 구현 문제 같다kipa00의 의견을 듣고 구현을 시작했습니다.

고민을 좀 하다가 홀수 열 홀수 행에 있는 원소들이 어떻게 이동할지를 생각해보니, 다 함께 움직인다는 사실을 파악했습니다. swap 쿼리가 고민이었는데 kipa00이 던져준 조언을 받아들여서 그대로 짰더니 예제가 거의 나왔습니다. 예제 2에서 출력되는 수의 순서가 살짝 이상하길래, 아무 생각 없이 코드에 있던 몇 개의 -를 +로 바꿨더니 예제가 잘 나왔습니다. 98분째에 제출한 다음 맞기를 기도한 결과 한 번에 맞았습니다. 한 번 틀리기 시작하면 말리기 좋은 문제라서 틀렸다면 정신적 고통이 엄청났을 것 같은데, 운이 좋았던 것 같습니다.

(1:38 ~ 2:08) 러버덕 + 기도

F를 풀고 나서도 B는 계속 kipa00이 구현하고 있었습니다. 쉬운 풀이가 있지 않을까 생각하고 던진 풀이는 안타깝게도 small to large를 생각해내지 못한 저희 팀에게는 \(O(NK)\) 풀이였고, 그 결과 \(\sqrt{N}\)을 기준으로 나눠, 무려 LCA까지 써가며 문제를 해결하는 \(O(N \sqrt{N \log N})\) 풀이를 구현하는 kipa00을 응원할 수밖에 없었습니다. 몇 번의 시행착오가 있었는데, 옆에서 함수를 하나하나 같이 보면서 문제점을 찾아 고쳤습니다. \(\sqrt{N}\)과 같이 산술-기하 평균 부등식으로 결정되는 기준을 잡을 때 저는 가장 큰 값에 대해 최적인 값을 고정값 기준으로 정합니다. 이러한 팁을 전달해주었고, 거기에 연산 속도에 대한 저의 감을 더해 200을 기준으로 잡자는 결론을 냈습니다. 그 결과 128분째에 AC를 받았습니다.

(2:08 ~ 3:00) 남은 문제 감상 및 휴식

남은 문제는 E G J였고, 하나씩 읽어봤습니다. E는 어려운 수학이거나 FFT일 거라 생각했고, 그걸 풀 기운이 남아있는 팀원은 없었습니다. GJ는 제 처음 느낌 그대로 풀 만한 문제가 아니었고, 풀 수 있다고 해도 본선 진출이 거의 확정된 시점에서 딱히 풀고 싶지는 않았습니다. 그래서 스코어보드를 보면서 놀았습니다. 그리고 kipa00에게 H번을 던져주고 풀이를 생각해보도록 했습니다. 처음에 H번이 생각보다 너무 안 풀려서 살짝 놀랐는데, kipa00이 푸는 데 걸린 시간을 보니, 제가 비정상적으로 빨리 풀이를 생각한 것이었습니다.

# 짧은 후기

문제를 잘 풀어낸 것 같아서 기쁩니다. B번 문제의 경우 최근 커뮤니티 대회의 트렌드였다고 하는데, 요즘 들어 커뮤니티 대회에 별로 참여를 못 해서 잘 몰랐던 것 같습니다. 트렌드를 따라가는 일은 정말 어려운 것 같습니다. H번 문제를 너무 재밌게 풀었고, 지금도 계속 문제를 풀어냈던 과정이 떠오를 정도로 인상적이었습니다. 다른 까다로운 문제들을 잘 풀어주고, 문제를 안 풀고 있을 때도 모니터로 제가 풀던 문제의 입력 형식과 예제를 띄워주고 러버덕이 되어준 팀원 kipa00, crescent_h에게 정말 고맙다는 말을 해주고 싶습니다. 그리고 앞으로도 애드 혹 강자로서의 입지를 더더욱 굳혀나가고 싶습니다. 본선도 화이팅!

'PS' 카테고리의 다른 글

SCPC 2021 Round 2 5번 문제 접근 과정과 풀이  (0) 2021.08.08
Google Hash Code 2020 대회 (간단한) 후기!  (0) 2020.02.21

첫 세 문제를 풀어내는 데 좀 많은 시간을 써서, 대회 시간 내에 풀지는 못했지만, 만약 저에게 2시간만 더 있었다면 이 문제를 풀어낼 수 있었을 겁니다...

1. 생각의 시작

일단 간단한 재귀를 생각해보는 것으로 출발했습니다. 대략 두 칸 넘어갈 때마다 횟수가 3배 정도 되는 방법들이 나왔는데, 가장 마지막에 생각했던 간단한 재귀는 다음과 같습니다.

1234567 - - → 127 - 3456 → 1 27 3456 → 13456 27 - → 1 234567 - → - 234567 1 → ...

한편 여기서 중요한 성질들을 조금 잡아냈습니다.

1. 반드시 "1 234567 -"이 되어야만 "- 234567 1"로 넘어가면서 진행할 수 있다.

2. 모든 이동은 가역적이기 때문에, "- - 1234567 → - 234567 1"의 과정은 앞에서 만든 것을 활용할 수 있으므로, 이 과정을 정확히 반대로 수행하면 "- 234567 1 → - - 1234567"을 만들 수 있다.

특히 2번 성질은 구현량을 효과적으로 줄여주기 때문에 매우 중요했습니다.

2. 좌절

위에 나와 있는 재귀의 식을 구해보면

f(n) = 2(g(n) + 2 + h(n)) + 1

g(n) = g(n-2) + 2 + h(n-2)

h(n) = h(n-2) + 1 + f(n-5) + 1 + h(n-2) + 1 + h(n-2)

정도의 식이 나왔습니다. 만점의 희망을 갖고 dp를 계산한 결과는..!

N=23 → 941745, N=24 → 1743913. 140점 정도짜리 풀이가 나왔습니다. 하지만 저는 부분 점수 따위에 만족하고 싶지 않았습니다. 그리고, 사실 "간단한 재귀"라고 했지만 구현이 생각보다 귀찮기 때문에, 완전한 풀이를 만들자고 결심했습니다. 뭐 결과적으로는 이게 실수였던 것 같습니다만... 저는 괜찮습니다. 이미 본선을 네 번이나 나갔기에 미련 없습니다.

3. 규칙 찾기

N=26일 때 만점을 받을 수 있는 길이와의 차이가 6배 이상 되었기 때문에, 방향을 선회해서 N이 작을 때의 최적해를 구해보고, 규칙을 찾아서 답을 만들어낼 수 있는 알고리즘을 구현해보기로 했습니다. BFS 코드에서 checker 코드를 만드는 것도 약간의 수정이면 가능해서, 만드는 김에 checker까지 만들었습니다.

from queue import Queue
N = 11

sd = [(0,1),(0,2),(1,0),(1,2),(2,0),(2,1)]
inv_sd = [2,4,0,5,1,3]
chkd = {}
def f(state, x):
    towers = [[],[],[]]
    for i in range(N):
        towers[state%3].append(i)
        state //= 3
    src, des = sd[x]
    nn, mm = len(towers[src]), len(towers[des])
    if nn == 0:
        return -1
    ppap = towers[src][nn//2]
    if (mm < 1 or towers[des][(mm-1)//2] < ppap) and (mm < 2 or ppap < towers[des][(mm-1)//2+1]):
        towers[src].remove(ppap)
        towers[des].insert((mm+1)//2, ppap)
    else:
        return -1
    nstate = 0
    for i in range(3):
        for j in towers[i]:
            nstate += i*(3**j)
    return nstate

def ppp(state):
    towers = [[],[],[]]
    for i in range(N):
        towers[state%3].append(i+1)
        state //= 3
    print(towers)

if __name__ == '__main__':
    start = 0
    end = (3**N-1)
    bfsq = Queue()
    bfsq.put(start)
    chkd[start] = (0, -1)
    while not bfsq.empty():
        tmp = bfsq.get()
        # print(tmp, chkd[tmp])
        if tmp == end:
            break
        prv = chkd[tmp][0]
        for i in range(6):
            ttmp = f(tmp, i)
            if ttmp != -1 and ttmp not in chkd:
                chkd[ttmp] = (prv+1, i)
                bfsq.put(ttmp)
    y = end
    ans = []
    aans = []
    while y!=start:
        tmp = chkd[y]
        ans.append(tmp[1])
        aans.append(y)
        y = f(y, inv_sd[tmp[1]])
    print(ans[::-1])
    print(''.join([chr(ord('A')+i) for i in ans[::-1]]))
    print(len(ans))
    idx = 0
    for z in aans[::-1]:
        idx += 1
        print(idx, end=' ')
        ppp(z)

# checker
# if __name__ == '__main__':
#     cur = 0
#     N = int(input())
#     with open("output.txt", "r") as ff:
#         ans = ff.readline().strip()
#     # ans = input()
#     cnt = 0
#     for c in ans:
#         xx = ord(c) - ord('A')
#         cur = f(cur, xx)
#         cnt += 1
#         # print(cnt, end=' ')
#         # ppp(cur)
#         if cur == -1:
#             print("boom")
#             exit(0)
#     print(cnt, end=' ')
#     ppp(cur)

이렇게 답을 뽑아놓고, 100개 정도 되는 타워들의 패턴을 보면서, 큰 덩어리가 움직이는 형태가 나오는지를 관찰했습니다. 특히, 예를 들면 N=7인 경우 1과 7이 항상 타워에 들어 있는 상황이라면, 1과 7이 없더라도 똑같은 이동 결과가 나오게 됩니다. 1234567 - - → 127 - 3456를 23456 - - → 2 - 3456과 똑같이 생각할 수 있다는 것이죠. 이것이 "간단한 재귀"에서 재귀 함수를 호출하게 되는 부분입니다. 심지어 이동도 똑같기 때문에 정답 string도 어느 기둥에서 어느 기둥으로 옮기는지만 잘 바꿔주면 됩니다.

1 [[1, 2, 3, 5, 6, 7], [4], []]
2 [[1, 2, 3, 6, 7], [4, 5], []]
3 [[1, 2, 6, 7], [4, 5], [3]]
4 [[1, 2, 7], [4, 5], [3, 6]]
5 [[1, 2, 5, 7], [4], [3, 6]]
6 [[1, 2, 5, 7], [], [3, 4, 6]]
7 [[1, 2, 7], [], [3, 4, 5, 6]]

8 [[1, 7], [2], [3, 4, 5, 6]]
9 [[1], [2, 7], [3, 4, 5, 6]]

10 [[1, 5], [2, 7], [3, 4, 6]]
11 [[1, 5], [2, 4, 7], [3, 6]]
12 [[1], [2, 4, 5, 7], [3, 6]]
13 [[1, 6], [2, 4, 5, 7], [3]]
14 [[1, 3, 6], [2, 4, 5, 7], []]
15 [[1, 3, 5, 6], [2, 4, 7], []]
16 [[1, 3, 5, 6], [2, 7], [4]]
17 [[1, 3, 6], [2, 7], [4, 5]]
18 [[1, 6], [2, 3, 7], [4, 5]]
19 [[1], [2, 3, 6, 7], [4, 5]]
20 [[1, 5], [2, 3, 6, 7], [4]]
21 [[1, 5], [2, 3, 4, 6, 7], []]
22 [[1], [2, 3, 4, 5, 6, 7], []]

23 [[], [2, 3, 4, 5, 6, 7], [1]]

24 [[], [2, 3, 4, 6, 7], [1, 5]]
25 [[4], [2, 3, 6, 7], [1, 5]]
26 [[4, 6], [2, 3, 7], [1, 5]]
27 [[4, 6], [2, 7], [1, 3, 5]]
28 [[4], [2, 6, 7], [1, 3, 5]]
29 [[], [2, 6, 7], [1, 3, 4, 5]]
30 [[6], [2, 7], [1, 3, 4, 5]]
31 [[6], [2, 4, 7], [1, 3, 5]]
32 [[], [2, 4, 6, 7], [1, 3, 5]]
33 [[3], [2, 4, 6, 7], [1, 5]]
34 [[3, 6], [2, 4, 7], [1, 5]]
35 [[3, 4, 6], [2, 7], [1, 5]]
36 [[3, 4, 5, 6], [2, 7], [1]]
37 [[3, 4, 5, 6], [2], [1, 7]]
38 [[3, 4, 5, 6], [], [1, 2, 7]]
39 [[3, 4, 6], [], [1, 2, 5, 7]]
40 [[3, 6], [4], [1, 2, 5, 7]]
41 [[3, 6], [4, 5], [1, 2, 7]]
42 [[3], [4, 5], [1, 2, 6, 7]]
43 [[], [4, 5], [1, 2, 3, 6, 7]]
44 [[], [4], [1, 2, 3, 5, 6, 7]]
45 [[], [], [1, 2, 3, 4, 5, 6, 7]]

N=7일 때를 간단히 보자면, 1234567 - - → 127 - 3456을 23456 - - → 2 - 3456로 환원할 수 있습니다. 또한, 이후 1 27 3456 → 1 234567 - 역시 1 - 3456 → 1 3456 -로 환원할 수 있습니다. 이렇게 하면 1234567 - - → 1 234567 -가 끝났고, 위에서 언급했듯이 전체 답도 만들어낼 수 있습니다.

많은 시행착오 끝에 다음과 같은 5가지 점화식으로 모든 것을 나타낼 수 있었습니다.

f(9,^,0,0) = f(7,^,0,0) + 2 + f(6,v,^,0) + 1 + f(6,^,v,0)
f(9,0,0,^) = f(8,v,^,0) + 1 + f(8,^,v,0)
f(9,^,v,0) = f(7,^,v,0) + 1 + f(6,0,0,^) + 2 + f(7,^,^,0)
f(9,^,^,0) = f(6,0,0,^) + 2 + f(5,^,^,0) + 1 + f(6,^^,0,^) + f(6,0,^^,^) + f(5,^,^,0) + 2 + f(6,0,0,^)
f(9,^^,0,^) = f(7,0,0,^) + 2 + f(6,^,^,0) + 1 + f(5,0,0,^) + 2 + f(4,^,^,0) + 1 + f(5,^^,0,^)

f(9,^,0,0): 123456789A - - => 1 23456789A -
f(9,0,0,^): 23456789A - 1 => - 23456789A 1
f(9,^,v,0): 123456789A B - => 1 23456789AB -
f(9,^,^,0): 13456789AB 2 - => 1 23456789AB -
f(9,^^,0,^): 13456789ABC - 2 => 13 456789ABC 2

실제 구현과는 조금 다른데, 세 번째 케이스가 바닥이 목표 기둥이 아닌 곳에 있어야 더 빠를 때가 있어서 해당 부분을 flag로 넣어 최적화를 했습니다.

저 점화식을 쓸 때 기둥 위치까지 같이 썼다면 좀 더 구현이 빨랐을 수도 있겠네요.

4. 구현, 그리고 대회 종료

구현을 하다가 결국 완성된 코드를 만들지 못하고 대회가 종료되었습니다. 이후 열심히 코딩을 했지만 2시간이 더 걸렸습니다. 아이디어 완성 이후부터만 따지더라도 전체 코딩 시간이 4시간은 넘는 것 같습니다.

처음에 N이 작은 경우에 대한 하드코딩을 좀 덜 했더니 1007835라는 조금 아쉬운 길이가 나왔습니다. 작은 경우에 대한 예외처리를 열심히 추가했더니 976371짜리 만점 답안이 나왔습니다. 출제자가 확인한 972051보다는 큰데, 더 줄이기 위해서는 아마 세 번째 케이스에 대한 완벽한 최적화가 필요할 것 같습니다.

아래는 답을 만드는 코드입니다.

#include <bits/stdc++.h>
using namespace std;
pair<int,int> i2p[6] = {{0,1},{0,2},{1,0},{1,2},{2,0},{2,1}};
map<pair<int,int>, int> p2i;
int ggr[6] = {2,4,0,5,1,3};
void init(){
	for(int i=0;i<6;i++) p2i[i2p[i]] = i;
}
string cv(string s, int a, int b, int c){
	string res = "";
	int k = s.size();
	int xxx[3] = {a, b, c};
	for(int i=0;i<k;i++){
		pair<int,int> tmp = i2p[s[i] - 'A'];
		res += (char)('A'+p2i[{xxx[tmp.first], xxx[tmp.second]}]);
	}
	return res;
}
string geoguro(string s){
	string res = "";
	int k = s.size();
	for(int i=k-1;i>=0;i--) res += (char)('A'+ggr[s[i]-'A']);
	return res;
}
string f1(int n, int a, int b, int c);
string f2(int n, int a, int b, int c);
string f3(int n, int a, int b, int c, bool flag);
string f4(int n, int a, int b, int c);
string f5(int n, int a, int b, int c);
int main(){
	// freopen("output.txt", "w", stdout);
	init();
	int N = 26;
	cout << "Case #1\n";
	cout << N << "\n";
	string ans;
	ans += f1(N-1, 0, 1, 2);
	ans += "B";
	ans += geoguro(f1(N-1, 2, 1, 0));
	cout << ans << "\n";
	cout << ans.size();
}
string f1(int n, int a, int b, int c){
	if(n==0) return "";
	if(n==1) return cv("A", a, b, c);
	if(n==2) return cv("AA", a, b, c);
	if(n==3) return cv("BAAF", a, b, c);
	if(n==4) return cv("BBAAEFA", a, b, c);
	string res = "";
	res += f1(n-2, a, c, b);
	res += (char)('A'+p2i[{a,b}]);
	res += (char)('A'+p2i[{a,b}]);
	res += geoguro(f3(n-3, a, c, b, true));
	res += f3(n-3, a, b, c, false);
	// cout << "f1 " << n << " " << a << " " << b << " " << c << ": " << res << "\n";
	return res;
}
string f2(int n, int a, int b, int c){
	if(n==0) return "";
	if(n==1) return cv("A", a, b, c);
	if(n==2) return cv("BAF", a, b, c);
	if(n==3) return cv("ABBCFFA", a, b, c);
	string res = "";
	res += geoguro(f3(n-1, c, a, b, false));
	res += f3(n-1, c, b, a, true);
	return res;
}
string f3(int n, int a, int b, int c, bool flag){
	if(n==0) return flag?"":cv("F", a, b, c);
	if(n==1) return flag?cv("DAF", a, b, c):cv("AF", a, b, c);
	if(n==2) return flag?cv("DAFA", a, b, c):cv("AFA", a, b, c);
	string res = "";
	if(flag){
		res += f3(n-2, a, c, b, false);
	}
	else{
		res += f3(n-2, a, c, b, true);
	}
	res += (char)('A'+p2i[{a,b}]);
	res += f2(n-3, c, a, b);
	res += (char)('A'+p2i[{c,b}]);
	res += (char)('A'+p2i[{c,b}]);
	res += f4(n-2, a, b, c);
	return res;
}
string f4(int n, int a, int b, int c){
	if(n==0) return "";
	if(n==1) return cv("A", a, b, c);
	if(n==2) return cv("BAF", a, b, c);
	if(n==3) return cv("ABBCFFA", a, b, c);
	if(n==4) return cv("BAABCDAECFAAF", a, b, c);
	if(n==5) return cv("ABDAAFBDCDAECEFBAAEFA", a, b, c);
	string res = "";
	res += f2(n-3, a, c, b);
	res += (char)('A'+p2i[{a,b}]);
	res += (char)('A'+p2i[{a,b}]);
	res += f4(n-4, c, b, a);
	res += (char)('A'+p2i[{a,c}]);
	res += f5(n-3, b, c, a);
	res += (char)('A'+p2i[{b,a}]);
	res += geoguro(f5(n-3, a, c, b));
	res += (char)('A'+p2i[{c,b}]);
	res += f4(n-4, a, c, b);
	res += (char)('A'+p2i[{a,b}]);
	res += (char)('A'+p2i[{a,b}]);
	res += f2(n-3, c, b, a);
	return res;
}
string f5(int n, int a, int b, int c){
	if(n==0) return "";
	if(n==1) return cv("BAE", a, b, c);
	if(n==2) return cv("ABAE", a, b, c);
	if(n==3) return cv("ABBAEF", a, b, c);
	if(n==4) return cv("BAFBBDAFEF", a, b, c);
	string res = "";
	res += f2(n-2, a, b, c);
	res += (char)('A'+p2i[{a,c}]);
	res += (char)('A'+p2i[{a,c}]);
	res += f4(n-3, b, c, a);
	res += (char)('A'+p2i[{a,b}]);
	res += f2(n-4, c, b, a);
	res += (char)('A'+p2i[{c,a}]);
	res += (char)('A'+p2i[{c,a}]);
	res += f4(n-5, b, a, c);
	res += (char)('A'+p2i[{c,b}]);
	res += f5(n-4, a, b, c);
	return res;
}

끝으로 마지막 디버깅 단계에서 썼던, 실제 이동 과정이 들어있는 메모를 첨부합니다.

# f5
13456...WXYZ - 2
13YZ 456..WX 2
1Z 456..WX 23Y
1Z 4 2356..WXY
1 4Z 2356..WXY
1 456..WZ 23XY
13X 456..WZ 2Y
136..WX 45Z 2Y
136..WX 45YZ 2
13456..WXYZ - 2

# f1
123..XYZ - -
12Z - 3..XY
1 2Z 3..XY
13..X 2Z Y
13..X 2YZ -
1 23..XYZ -

# f3
1234..XY Z -
1234..XY - Z
12Y - 34..XZ
1Y 2 34...XZ
14..XY 2 3Z
14..XY 23Z -
1 234..XYZ -

# f2
2..YZ - 1
Z - 12..Y
- Z 12..Y
- 2..YZ 1

# f4
1345..XYZ 2 -
13YZ 2 45..X
1Z 23Y 45..X
1Z 235..XY 4
1 235..XY 4Z
1 23 45..XYZ
13 2 45..XYZ
135..XY 2 4Z
135..XY 2Z 4
13Y 2Z 45..X
1 23YZ 45..X

8월 24일에 AIM Tech math quiz라는 게 열렸습니다. 수학을 좋아하는 저로서 참가를 안 할 수가 없기에, cs71107님과 팀을 이루어 참가했습니다. (팀 이름이 제 이름인 건 함정(?)입니다) 총 30문제였고, 대회는 2시간 동안 진행되었고, 스코어보드는 다음과 같습니다.

 

https://mathmaker.ru/contests/12eaac24-15d2-4d19-9afb-6e303ecba55e/2/standings

 

Math Platform

 

mathmaker.ru


30문제 중 24문제를 풀었고, 참가팀 35팀 중 7등을 했습니다. 그래도 대회 시간 동안 풀 만한 문제는 다 풀지 않았나 하는 생각은 듭니다. 문제들의 난이도는 5초 만에 풀 수 있는 문제부터, 아직도 완벽한 풀이를 모르는 문제까지 다양합니다. 업솔빙은 아래 링크에서 할 수 있습니다.

 

https://mathmaker.ru/contests/12eaac24-15d2-4d19-9afb-6e303ecba55e/3/1

 

Math Platform

 

mathmaker.ru

 

아래는 문제별 풀이입니다. 풀이에서 답을 안 써놓은 경우도 있는데, 답은 여기서 확인할 수 있습니다.

 

체감 난이도는 다음과 같습니다. 단순 계산이나 간단한 수학 지식에 익숙하신 분들은, 앞의 몇 문제는 건너뛰셔도 상관 없을 것 같습니다. 참고로 대회 때 저희 팀이 못 푼 문제는 19 20 21 27 28 30입니다.

 

1 2 3 4 5 6 8 9 10 / 7 11 12 13 14 15 16 17 18 / 22 23 26 / 24 25 27 29 / 19 20 21 28 30

 

1. What is the largest number which can be drawn using 15 matches?

번역: 15개의 성냥개비로 만들 수 있는 가장 큰 수는?

더보기

자릿수가 많으면 많을수록 좋을 것 같습니다. 그리고 큰 숫자가 앞에 있을수록 좋을 것 같습니다.

 

2. Find sum of the series \(\frac{1}{1 \cdot 4} + \frac{1}{2 \cdot 5} + \frac{1}{3 \cdot 6} + \frac{1}{4 \cdot 7} + \cdots \).

더보기

\(\frac{1}{n\cdot (n+3)} = \frac{1}{3}(\frac{1}{n} - \frac{1}{n+3})\)을 이용하면 될 것 같습니다.

 

3. The tractor began to plow the field, after some interval of time second tractor of the same kind joined it, then after the same interval - third, and so on until the last. It turned out that the first tractor worked five times longer than the last. If everyone had started at the same time, they would have done the work in 2 hours. How long had the first tractor actually worked?

번역: (동일한) 트랙터 3대가 일을 하는데, 첫 번째 트랙터가 일을 시작한 뒤, T분 뒤에 두 번째 트랙터가 일을 시작했고, 또 그 다음 T분 뒤에 세 번째 트랙터가 일을 시작했습니다. 일이 끝났을 때, 첫 번째 트랙터는 세 번째 트랙터보다 5배 더 일했습니다. 트랙터 3대가 동시에 일을 시작했다면 일은 2시간만에 끝났을 것입니다. 원래 상황에서 첫 번째 트랙터는 얼마나 일했을까요?

더보기

굳이 풀이를 쓰자면, 세 트랙터가 일한 시간을 5t, 3t, t라 놓으면 9t=120*3입니다.

 

4. The sum of infinite geometric series containing positive numbers is \(S\). The second term of the sequence is \(1\). Find the smallest possible value of \(S\).

번역: 양수만으로 이루어진 무한등비급수의 합이 \(S\)이고, 두 번째 항이 \(1\)입니다. \(S\)의 최솟값은?

더보기

수열의 공비를 \(r\)이라 놓으면 \(0<r<1\)이어야 합니다. 한편 초항은 \(\frac{1}{r}\)이므로 \(S=\frac{1}{r(1-r)}\)입니다. 이 값을 최소화하려면 분모를 최대화해야 합니다. 산술-기하 평균 부등식을 써도 되고, 이차함수의 최댓값을 구해도 됩니다.

 

5. Each of the 12 men is either a knight (always tells truth) or a liar (always tells lie). One of them said: "The number of liars among us is divisible by 1"; the second one said: "The number of liars among us is divisible by 2"; ...; the last one said: "The number of liars among us is divisible by 12". How many knights can be in this group?

번역: 12명의 사람이 있는데, 참말만 하는 참말쟁이거나 거짓말만 하는 거짓말쟁이입니다. 사람들에게 1부터 12의 번호를 매겼을 때, k번 사람은 "우리 중 거짓말쟁이의 수는 k의 배수야" 라고 말했습니다. 참말쟁이의 수로 가능한 값을 모두 구하세요.

더보기

거짓말쟁이의 수를 고정해놓고 나면, 누가 참말을 했는지가 정해집니다. 그렇게 정하고 나서 실제로 거짓말쟁이의 수가 고정해놨던 그 수인지를 확인하면 됩니다.

혹시 틀렸다면, edge case가 있지는 않을지 한 번 더 생각해봅시다.

 

6. An ant travels through the vertices of heptagon. Every second he moves to one of the two neighboring vertices with equal probability. Find probability that the ant will finish in the initial vertex after 9 moves.

번역: 개미가 7각형의 꼭짓점들을 돌아다니고 있습니다. 각 이동에서 개미는 이웃한 두 꼭짓점 중 랜덤으로 하나의 꼭짓점으로 갑니다. (양쪽 점으로 갈 확률은 0.5로 같습니다) 9번의 이동 이후 시작점으로 되돌아올 확률을 구하세요.

더보기

시작점으로 돌아오려면 8번 오른쪽, 1번 왼쪽으로 가거나, 1번 오른쪽, 8번 왼쪽으로 가야 합니다. 각각의 경우의 수는 9이고, 전체 경우의 수는 \(2^9=512\)입니다.

 

7. 20 teams participate in tournament. Every two teams play exactly one game between each other, winner receives 3 points, loser 0 points, in case of tie both teams receive 1 point. After the tournament all teams have been sorted by the number of points in descending order. What is the maximum possible point difference between two neighboring teams in such list?

번역: 20팀이 리그전에 참가합니다. 임의의 두 팀끼리는 정확히 한 번 맞붙고, 이기면 3점, 지면 0점, 비기면 1점입니다. 경기가 모두 끝나고 승점 순서대로 정렬했을 때, 이웃한 두 팀의 승점 차이로 가능한 값 중 최댓값은 얼마일까요?

더보기

가능한 최대 승점은 57점입니다. 이 때 나머지 팀들의 승점의 총합을 생각해보면, 경기마다 승점의 총합은 2 또는 3씩 늘어나므로, 최소 2 * (나머지 경기 수)는 됩니다. 이게 최대한 고르게 분배되어있을 때가 1등과 2등의 차이가 최대가 됩니다. 다른 등수 차이를 가장 큰 차이로 만들어보려 해도 이 때의 차이를 만들 수 없음을 확인할 수 있습니다.

 

8. Find the largest integer number \(n\) such that \(98!+99!+100!\) is divisible by \(5^n\).

번역: \(98!+99!+100!\)이 \(5^n\)의 배수인 최대의 \(n\)을 구하세요.

더보기

일단 식을 \(98!\)으로 묶으면 \(98!(1+99+100)\)이 됩니다. \(98!\)의 소인수분해에 5가 몇 번 나오는지 알 수 있다면 문제를 풀 수 있겠습니다.

 

9. Number \(m\) is chosen uniformly randomly from the set \(\{11,13,15,17,19\}\), and number \(n\) is chosen uniformly randomly from set \(\{1999,2000,\cdots,2017,2018\}\). What is the probability that the last digit of \(m^n\) is \(1\)?

번역: \(m\)은 집합 \(\{11,13,15,17,19\}\)에서 균일하게 랜덤으로 선택되며, \(n\)은 집합 \(\{1999,2000,\cdots,2017,2018\}\)에서 균일하게 랜덤으로 선택됩니다. \(m^n\)의 마지막 자리가 1일 확률은 얼마일까요?

더보기
거듭제곱의 끝자리는 4를 주기로 반복됩니다. 마침 \(n\)을 선택하는 집합이 연속한 20개의 수를 담고 있으니, 각각의 수별로 그 4-주기 안에 1이 몇 번 나오는지를 확인하면 충분합니다.

 

10. For integer \(n\ge 2\), define \(f(n)\) as the largest integer \(m\) such that \(\sqrt[m]{n}\) is integer number. Find the value of sum \(f(2)+f(3)+\cdots + f(100)\).

더보기

\(k\)를 고정해놓고, \(f(x)=k\)인 \(x\)의 개수를 세면 편합니다. 제곱수라고 다 \(f(x)=2\)가 아닌 것만 조심하면 됩니다.

 

11. The number is called a palindrome if it is read the same in both directions (the number cannot start from 0). Vasya randomly uniformly chooses a 6-digit palindrome \(n\). What is the probability that the number \(\lfloor\frac{n}{11}\rfloor\) is also a palindrome?

더보기

대회 때는 그냥 코드 짜서 풀었습니다. 코딩 없이 풀자면, 어차피 개수만 세면 되고, 6자리 팰린드롬은 언제나 11의 배수니까, 5자리 팰린드롬 중에서 11을 곱해도 팰린드롬인 것의 개수를 세는 편이 생각하기 편합니다. 하다 보면 받아올림이 없으면 되는 것 같고, 받아올림이 없는 경우들만이 가능한 경우임을 알 수 있습니다. 개수를 셀 때는 가운데 수를 기준으로 세면 나름 규칙적으로 셀 수 있습니다. leading zero가 없어야 함은 언제나 주의하면 될 것 같습니다.

 

12. Tennis balls of radius 1 are packed in cylinder containers of radius 2 and height 1000. Find the maximum number of balls which can be packed in one container.

번역: 반지름이 2이고 높이가 1000인 원기둥에 반지름이 1인 공이 최대 몇 개 들어갈까요?

더보기

가로로 2개, 세로로 2개, 이런 모양이 반복됩니다. 이 때 두 층마다 중심들을 이은 도형은 정사면체가 됩니다. 정육면체의 이웃하지 않은 점 4개를 고르면 정사면체가 된다는 사실을 잘 생각해보면, 한 변의 길이가 2인 정사면체에서 마주보는 두 모서리의 거리는 \(sqrt{2}\)가 됨을 알 수 있습니다. \(sqrt{2}(x-1)+2 \le 1000\)을 풀면 층의 개수 \(x\)가 나옵니다.

 

13. Let \(\mathcal{P}\) be a parallelepiped (not necessarily rectangular) with sides \(x,y,z\). Let the 4 spatial diagonals \(\mathcal{P}\) have lengths \(15,17,21,23\). Find \(x^2+y^2+z^2\).

번역: 평행육면체의 세 변의 길이가 \(x,y,z\)이고, 면에 포함되지 않는 대각선 네 개의 길이가 각각 \(15,17,21,23\)입니다. \(x^2+y^2+z^2\)의 값을 구하세요.

더보기

평행육면체를 이루는 세 벡터를 \(v_1, v_2, v_3\)라 합니다. 이 때 각각의 대각선은 \(v_1 \pm v_2 \pm v_3\)로 나타낼 수 있습니다. 벡터의 길이의 제곱은 벡터 스스로를 내적한 값과 같으니까, 대각선 네 개의 길이의 합은 \((v_1+v_2+v_3)^2 + (v_1+v_2-v_3)^2 + (v_1-v_2+v_3)^2 + (v_1-v_2-v_3)^2 = 4(v_1^2+v_2^2+v_3^2)\)입니다.

 

14. Find the smallest 3-digit divisor of number \(10\cdots010\cdots01\). 0이 100개씩 있는 이 수의 약수인 세 자리 수 중 가장 작은 수를 구하세요.

더보기
다항식 \(x^202+x^101+1\)이 \(x^2+x+1\)의 배수라는 사실로부터 111이 이 수의 약수라는 사실을 알 수 있습니다. 101, 103, 107, 109가 이 수의 약수가 아닌지는 확인해봐야 하기는 하는데, 뭐 약간의 느낌이 들지 않나요?

 

15. At McDonald's, nuggets are sold in packs of \(9\) or \(20\). Find the maximum number \(n\) such that it is impossible to buy exactly \(n\) nuggets.

더보기

나름 유명한 문제인 데다가 closed-form formula가 있습니다. \(a,b\)가 서로소일 때 \(g(a,b)=ab-a-b\)입니다. 증명도 어렵지는 않으니 한 번 해 보는 것도 좋습니다. 참고로, 묶음의 단위가 3개 이상인 경우에 대해서는 일반적인 식은 알려져 있지 않습니다.

 

16. Consider function \(f(x) = \sum_{k=2}{10} (\lfloor kx \rfloor - k \lfloor x \rfloor)\), where \(\lfloor x \rfloor\) is the largest integer number which is not greater than \(x\). How many different values does the function \(f\) take for \(x \ge 0\)?

번역: 저 함수가 가질 수 있는 서로 다른 값의 개수는?

더보기

일단 \(x\)의 소수부만 중요합니다. \(x\)를 \(0\)에서 \(1\)까지 천천히 올린다고 생각하면, 값이 변하는 곳은 분모가 10 이하인 분수들임을 알 수 있습니다. 그러한 분수의 개수를 세면 되는데, 중복된 수는 한 번만 세야 하므로 기약분수를 셉니다. 오일러 \(phi\) 함수와 관련이 있는 것 같습니다.

 

17. Represent number 2020 as a fraction \(\frac{a_1!a_2!\cdots a_m!}{b_1!b_2!\cdots b_n!}\), where \(a_i, b_j\) are integer numbers, \(a_1>a_2>\cdots>a_m>0, b_1>b_2>\cdots>b_n>0\) and the value of \(a_1+b_1\) is the minimum possible. Find the value \(|a_1-b_1|\).

번역: 2020을 저런 형태의 식으로 나타내는데, \(a_1+b_1\)이 최소가 되도록 합니다. 이 때 \(|a_1-b_1|\)을 구하세요.

더보기

분자에 \(101\)이 소인수로 있어야 합니다. 그러므로 \(a_1\)은 최소 101입니다. 그러면 분모에 \(97\)이 소인수로 있어야 합니다. 그러므로 \(b_1\)은 최소 97이고, 남은 곱을 \(\frac{(n-1)!}{n!}\) 꼴로 잘 처리하면 이게 가능하다는 걸 확인할 수 있습니다.

 

18. Consider points in the plane \((x,y)\) with integer coordinates such that \(x^2+y^2=2^k\) where \(0 \le k \le 1000\) and one more point - \(0,0\). Find the number of squares having all vertices at these points.

더보기

일단 이 점들은 \((0,0), (0,2^k), (2^k,0), (2^k,2^k)\)뿐입니다. (증명의 흐름: 만약 \(k\)가 \(2\) 이상이면 \(x,y\) 모두가 짝수여야 하므로 양변을 \(4\)로 나누는 과정을 반복할 수 있고, 따라서 \(k=0, k=1\)의 base case만 남습니다.) 그리고 생기는 정사각형들이 매우 규칙적으로 나올 수밖에 없습니다.(증명... 하고 싶으신가요...?) 잘 세주면 되는데, \((0,0)\)을 중심으로 갖는 것들과, \((0,0)\)을 꼭짓점으로 갖는 것들을 나누어 세면 됩니다.

 

19. Rain has 3 eggs. One of them is rubber and can't crush. Unfortunately, Rain doesn't know which one. He is in a 100-storey skyscraper. If you throw a normal egg from the top floor, it'll break. How many times Rain should throw an egg to clearly identify the minimum floor from which an egg breaks?

The rubber egg can be thrown as many times as you like, the unbroken egg can be thrown again. An egg breaks if and only if it is thrown from the floor \(y\ge x\) where \(x\) is the number of the minimum such floor.

번역: 3개의 달걀이 있는데 하나는 가짜라 안 깨집니다. 가짜가 뭔지는 모릅니다. 100층짜리 빌딩에서 실험을 통해 계란이 깨지는 최초의 층 \(x\)을 알아내려 합니다. 100층에서 달걀이 깨진다는 사실은 알려져 있으며, 현재 층 \(y\)가 \(x\) 이상일 때에만 (정상) 계란은 깨집니다. 계란을 던지는 최소 횟수는 얼마일까요?

더보기

처음에 생각했던 아이디어는, 두 개씩 떨어뜨리면 깨지는지 아닌지를 확실히 알 수 있으니까, \(x_1, x_2, \cdots, x_k\)에서 달걀을 두 개씩 떨어뜨린다는 것이었습니다. 이 때 \(2x_1-1, 2(x_2-x_1)-1 + 2, 2(x_3-x_2)-1 + 4, \cdots, 2(x_k-x_{k-1})-1+2(k-1), 2k\)가 모두 어떤 값 이하면 됩니다. 이걸 \(2k\)로 잡느냐 \(2k+1\)로 잡느냐 두 가지를 해 보고 그 중 작은 값이 답이 아닐까 했는데 계산을 잘못해서 27을 냈습니다. 이렇게 했을 때 답은 28이고 대회 중에 답은 실제로 28이었습니다. 그런데 실제로는 (3개의 달걀을 잘 구분하고 있을 수 있다는 가정 하에) 더 좋은 방법이 있습니다. 한 층에 두 개씩 떨구는 게 좀 비효율적이니까, 그냥 \(x_1, x_2, \cdots, x_k\)에 하나씩 떨구되, 1번, 2번, 3번 달걀을 번갈아서 던지는 것입니다. 이 때 어떤 달걀이 \(x_i\)에서 깨졌다면, \(x_{i-1}\)에서 안 깨졌던 달걀은 \(x_{i-2}+1\)부터 \(x_{i-1}\)까지에서도 안 깨지는 게 당연하므로 굳이 다시 던질 필요가 없습니다. 그래서 \(x_{i-2}+1\)부터 \(x_{i-1}\)까지는 다른 달걀만 던지고, \(x_{i-1}+1\)부터 \(x_i-1\)까지는 두 달걀을 모두 던져보면 됩니다. 그래서 이 값이 \(k+1\) 이하가 되도록 잘 정해보면 되고, 그렇게 했을 때 \(x_k\)가 99 이상이 될 수 있다면 그 때의 \(k\)가 답이 됩니다. 시행착오를 통해 이 값이 23이라는 사실을 확인할 수 있습니다. 그 때의 최적의 \(x_i\) 수열은 다음과 같습니다.

\(12, 18, 26, 33, 40, 46, 52, 58, 63, 68, 73, 77, 81, 85, 88, 91, 94, 96, 98, 100, 101, 102, 103\)

 

20. A convex hexagon is circumscribed around a circle of radius \(1\). Consider three line segments connecting the midpoints of opposite sides of the hexagon. For which largest \(r\) can we assert that at least one of these segments is at least \(r\)?

번역: 반지름이 1인 내접원을 가지는 육각형 ABCDEF이 있습니다. 마주보는 변(AB-DE, BC-EF, CD-FA)의 중점을 연결한 세 선분을 생각합니다. 세 선분 중 최소 하나는 \(r\) 이상임을 보장할 수 있는 최소의 \(r\)은 얼마일까요? (=세 선분 중 최대 길이의 하한은 얼마일까요?)

더보기
왜 최댓값의 최솟값이라 하지 않고 최댓값의 하한과 같은 식으로 정의가 된 것일까요? 결론부터 말하자면, 그 하한이 unreachable하기 때문입니다. 이런 문제들은 언제나 극한을 생각해봐야 합니다. 정육각형이면 이 값이 2가 되고, 이게 최소가 아닐까? 하고 생각할 수도 있지만, 생각보다 접점들이 한쪽에 몰려있는 쪽에서 더 좋은 답이 나옵니다. 이걸 캐치하는 게 이 문제의 핵심이고, 그래서 저는 이 문제가 제일 어려웠던 것 같습니다. 여전히 증명은 못했지만, 답에 대한 직관?은 있습니다.(그리고 이 답이 대회 쪽에서 제공하는 답과 일치합니다) 접점 두 개를 뭉쳐보고, 세 개를 뭉쳐볼 때까지는 별 일이 없다가, 접점 네 개를(!) 뭉쳐놓고 나면 이 다각형은 아래 그림과 같이 정삼각형의 한 변의 중점에 점 세 개가 모여 있는 육각형이 됩니다. 그림에서처럼 \(AB, BC, CD, DE, EF, FA\)의 중점을 각각 \(P, Q, R, S, T, U\)라 하면, \(A, B, C, P, Q\)가 동일한 점이 되고, 중점을 연결한 선분 세 개는 아래의 빨간 선들 \(PS, QT, RU\)가 되며, 길이는 모두 \(\sqrt{3}\)입니다. 그리고 이것이 답입니다. 혹시 증명에 대한 아이디어가 있으시다면 제보해주시면 감사하겠습니다.

 

21. A jeweler made an open chain from \(2020\) consequently numbered rings. The capricious customer demanded to change the order of the rings in the chain. She ordered such an open chain so that the jeweler has to unlink as many rings as possible. How many rings does the jeweler have to unlink?

An unlinked ring can be disconnected from its neighbors in the chain and then linked together with other rings. For example, in the picture the unlinked are rings 2 and 5, and the chain on the left can be obtained by reversing the (3, 4) sub-chain and changing the order of other rings, while the chain to the right cannot be produced since in order to connect rings 1 and 3 one needs to unlink at least one of them, which is not the case.

번역: \(2020\)개의 고리가 \(1, 2, \cdots, 2020\) 순서로 일렬로 연결되어 있습니다. 어떤 고객이 이 chain의 순서를 특정 순서로 바꿀 것을 요구하였습니다. 고객이 요구한 대로 바꾸기 위해서는 고리를 몇 개 끊어야 합니다. 끊지 않은 고리는 다른 끊지 않은 고리와 연결할 수 없지만, 연결된 고리의 순서를 역순으로 바꾸는 것은 가능합니다. 그렇다면 각 배치마다 끊어야 하는 최소 고리의 개수가 나올텐데, 이 개수의 최댓값은 얼마일까요?

더보기

문제 이해가 쉽지 않습니다. 그림을 보고 대략 이해하면 되는데, 끊지 않은 고리끼리 연결할 수 없다는 게 핵심입니다. 일단 기본적으로 원래 배치에서 이웃했던 고리들이 절대 이웃하지 않도록 할 것입니다. 문제를 다음과 같이 접근합니다.

"보석 상인이 끊지 않을 고리를 정해갑니다. 이 때, 그 고리를 끊지 않도록 결정하게 되면서, 끊을 수밖에 없는 고리들이 생깁니다.(그 고리들은 원래 배치에서 그 고리와 이웃했던 고리들과, 나중 배치에서 그 고리와 이웃한 고리들입니다) 이것들을 제외하고, 다시 끊지 않을 고리를 정하고, 이 과정을 반복합니다."

이렇게 했을 때, 제외되는 고리의 개수는, 보석 상인이 최선을 다한다면 (끊지 않기로 선택한 고리 포함) 최대 4개입니다. 즉 4개를 제외할 때마다 최소 하나는 안 끊는다는 것입니다. 어떻게 최선을 다하는지는, 이미 제외하기로 결정된 고리와 이웃한 고리 or 양 끝에 있는 고리를 고르면 됩니다. 한편, 고객이 최선을 다하면, 어떻게 고리를 선택하더라도 4개 이상 지워지도록 할 수 있습니다. (1-2-3-4) -> (3-1-4-2)는 어떤 고리를 끊지 않기로 결정하는 순간 다른 모든 고리를 끊어야 하는 배치입니다. 이걸 반복하면 됩니다. (1-2-3-4-5-6-7-8) -> (3-1-4-2-7-5-8-6)과 같이 말이죠. 답은 뭘까요? 4개마다 3개를 지워야 하니까 \(2020\cdot 3/4\)겠군요.

 

22. Consider permutations of integer numbers from \(1\) to \(10^9\). Denote the number of odd permutations without fixed points by \(O\), and the number of even permutations without fixed points by \(E\). Find the value of \(E-O\).

번역: \(1\)부터 \(10^9\)의 순열을 생각합니다. 홀순열인 교란순열의 개수를 \(O\), 짝순열인 교란순열의 개수를 \(E\)라 할 때, \(E-O\)를 구하세요.

교란순열: 모든 \(i\)에 대해 \(i\)번 위치가 \(i\)가 아닌 순열

홀순열, 짝순열: 순열을 항등 순열(\(1, 2, \cdots, n\) 순서로 된 순열)로 만들기 위한 교환 횟수는 어떻게 교환하더라도 홀짝성이 변하지 않는데, 그 교환 횟수가 홀수면 홀순열, 짝수면 짝순열입니다.

더보기

순열에서 교환을 한 번 할 때마다 홀짝성이 바뀜에 주목합니다. \(1\)부터 \(n\)까지의 순열에 대해 \(E-O\)의 값을 \(x_n\)이라 합니다. \(n\ge 3\)일 때, 각 교란순열에 대해 \(1\)과 \(2\)의 위치를 바꿉니다. 바꾼 것도 교란순열인 경우, 둘 중 하나는 홀순열, 다른 하나는 짝순열이므로 \(x_n\)을 계산할 때 상쇄됩니다. 바꾼 것이 교란순열이 아닌 경우가 있는데, \(1\)번 위치가 \(1\)이어서 그럴 수도 있고, \(2\)번 위치가 \(2\)가 아니어서일 수도 있고, 둘 다일 수도 있습니다. \(1\)번 위치는 \(1\)이고 \(2\)번 위치는 \(2\)가 아닌 경우, \(1\)을 제외한 나머지가 교란순열입니다. 여기서 \(E-O\)가 \(x_{n-1}\)이니까, 원래에서 \(E-O\)는 (한 번 바꿨으므로) 부호가 바뀌어 \(-x_{n-1}\)만큼 기여합니다. 마찬가지로 나머지 경우에서 각각 \(-x_{n-1}, -x_{n-2}\)의 기여를 받으므로, 최종적으로 \(x_n=-2x_{n-1}-x_{n-2}\)입니다. 그리고 \(x_1=0, x_2=-1\)입니다. 이 점화식을 풀면(실제 대회 때는 팀원이 규칙을 찾아서 일반항을 구했습니다만) \(x_n=(-1)^{n-1} \cdot(n-1)\)입니다.

 

23. Sequence \(\{a_n\}\) is defined by the following recurrent rule: \(a_0=1, a_1=\sqrt[19]{2}, a_n = a_{n-1}^2 a_{n-2}\) for all \(n \ge 2\). Find the smallest integer \(k\) such that \(a_1a_2\cdots a_k\) is integer number.

더보기
\(b_n = \log a_n\)으로 두면 그냥 선형 점화식을 푸는 문제가 됩니다. 그렇게 하면, \(b_n = \frac{2^n-(-1)^n}{57}\)이 정수가 되는 최소의 \(n\)을 찾는 문제가 됩니다. 2는 소수 19에 대한 원시근이라는 것 같네요.

 

24. There are exactly \(77000\) quadruples \(a,b,c,d\) such that \(gcd(a,b,c,d)=77\) and \(lcm(a,b,c,d)=n\). What is the minimum possible value of \(n\)?

더보기

뭔가 소인수분해를 해서 각 소인수를 잘 분배해야 할 것 같습니다. 지수가 \(k\)일 때 경우의 수가 \(12k^2+2\)가 나옵니다. 이 값들을 잘 모아보면 \(14, 50, 110, 194, 302, \cdots\)인데, 앞의 세 개를 곱하면 \(77000\)이 됩니다. 그래서 답은 \(77\cdot 2^3 \cdot 3^2 \cdot 5\)입니다.

 

25. Consider parking for 2020 cars. All spots are situated along the line and are numbered from 1 to 2020. Arriving cars park as follows: the first car chooses the place uniformly randomly. Each next car selects the place from which the distance to the nearest car will be the greatest (one of such places with equal probability). Find the probability that the last (2020-th) car will occupy parking spot with number 1.

번역: 2020대의 차가 주차를 하는데, 첫 번째 차는 아무 데다 대고, 그 다음 차부터는 가장 가까운 차와의 거리가 최대가 되도록 하는 위치 중 하나를 랜덤으로 골라서 댑니다. (모든 랜덤은 uniform) 마지막 차가 1번 위치에 댈 확률은?

더보기

첫 차가 어디에 대느냐가 많이 중요합니다. 1011~2020에 대면 그 다음에 대는 곳이 바로 1번이 되니까 안 되는데, 앞쪽에 대도 생각보다 문제가 됩니다. 심지어는 3번에 대더라도, 2번보다는 1번을 무조건 먼저 대게 되어 있어서 마지막 위치가 1번이 될 수가 없습니다. 결국 첫 차가 2번에 대야만 한다는 결론이 나옵니다. 그 다음은 약간의 노가다인데, 가장 가까운 거리가 1이 되기 전까지 차를 열심히 댑니다. 그러면 자리가 몇 개 남을까요? 1번을 제외하고 1024개가 남습니다. (1024가 뭔가 매우 익숙한 수라는 생각이 들었을 수도 있는데, 조금 놀랍게도(?) 어떤 수에서 시작해도 2의 거듭제곱으로 끝납니다!) 이제 1025개 중에서 1번이 제일 마지막일 확률은 1/1025입니다. 이 확률을 첫 차가 2번에 댈 확률 1/2020과 곱해주면 됩니다.

 

26. Lets denote the distance between 5-digit numbers \(\overline{a_1a_2a_3a_4a_5}\) and \(\overline{b_1b_2b_3b_4b_5}\) as the largest \(i\), for which \(a_i\neq b_i\). All 5-digit numbers are written down in some order. What is the smallest possible sum of distances between adjacent numbers?

더보기

뒤집은 수가 작은 것부터 큰 순서대로 놓으면 됩니다. 이 때가 최솟값이라는 것은, \(a_5\)는 최소 9번 변해야 한다는 사실, 그리고 각 \(a_5\)가 같은 구간마다 \(a_4\)가 9번씩은 변해야 한다는 사실 등등을 종합해보면 알 수 있습니다.

 

27. Find the maximum size of the square, which can be placed inside the cube with edge of length 1.

더보기

삼수선의 정리 같은 걸 생각하다 보면, 정사각형을 어떤 한 면에 정사영시켜 직사각형을 만들었을 때, 그 직사각형이 정사각형 안에 들어가면 된다는 사실을 알 수 있습니다. 이 직사각형의 이웃한 두 변의 길이는 각각 \(b, \sqrt{1+b^2}\)이 되며, 가능한 \(b\)의 최댓값은 \(\frac{\sqrt{2}}{4}\)입니다. 이 때 원래 정사각형의 한 변의 길이는 \(\frac{3\sqrt{2}}{4}\)입니다.

 

28. Each of the 2020 cards has a number written on it, all these 2020 numbers are different. The cards are turned upside down. In one move, it is allowed choose ten cards, and in response, one of the numbers written on them will be reported (it is not known which one). What is the largest \(t\), such that it is guaranteed to be able to find \(t\) cards, about which it is known what number is written on each of them?

번역: 2020장의 카드가 있습니다. 각 카드에 쓰여 있는 수는 모두 다릅니다. 10장의 카드를 고르면 judge는 그 중 하나의 카드에 쓰여 있는 수를 알려줍니다. (어느 카드인지는 모릅니다) 최악의 상황에서도 \(t\)장의 카드에 쓰여 있는 수를 정확하게 알 수 있다 할 때 \(t\)는 최대 얼마일까요?

더보기

참가자의 입장에서는 몇 번을 물어봐도 되니까 가능한 모든 조합을 물어봅니다. judge의 입장에서는 정보를 많이 줄수록 불리하므로 각 조합에 대해 무조건 정해진 수를 알려줍니다. judge의 입장에서 생각하면 몇 개까지의 수를 숨길 수 있느냐 하는 문제가 됩니다. 그런데 숨기는 수들 중에서만 10개를 골랐을 때도 judge는 답변을 해야 하기 때문에, 결국 어떤 수가 어느 위치에 있는지를 완전히 숨길 수 있는 \(n\)의 최댓값을 구하면 됩니다. 좀 많이 어려운데, 비둘기집을 만든다고 생각합니다. 조금 작은 경우를 보면, 2개의 카드를 고르는 상황을 생각하면 \(n=3\)일 때는 숨길 수 있습니다. \((1,2):x_1,(2,3):x_2,(3,1):x_3\) 이렇게 주면 참가자는 \((x_1, x_2, x_3)\)와 \((x_3, x_1, x_2)\)의 상황을 구분할 수 없습니다. \(n=4\)일 때는 숨길 수 없음을 확인할 수 있습니다. 이제 10개를 고르는 상황에서는, 저런 삼각형 고리를 9개 만들어 놓으면, 10개를 고르면 비둘기집 원리에 의해 어떤 고리에서는 2개를 고르게 되어 있습니다. 그 고리 내에서의 규칙에 따라 수를 공개합니다. 이렇게 하면 27개를 완전히 숨길 수 있고, 수학적 귀납법으로 이 값이 최대임도 증명할 수 있습니다. 구하려는 답은 \(2020-27\)입니다.

 

29. Let \(a, b, c, d\) be positive real numbers which satisfy the following system of equations

\((a+b)(c+d)=143, (a+c)(b+d)=150, (a+d)(b+c)=169\).

Find the minimum possible value of \(a^2+b^2+c^2+d^2\).

더보기

\(a+b+c+d=t\)라고 놓습니다. 그러면 \((a+b)\)와 \((c+d)\)는 합이 \(t\)이고 곱이 \(143\)인 두 수입니다. 그러므로 \((a+b)^2+(c+d)^2=t^2-286\)입니다. 마찬가지로 \((a+c)^2+(b+d)^2=t^2-300, (a+d)^2+(b+c)^2=t^2-338\)입니다. 세 식을 다 더하면 좌변에 \((a+b+c+d)^2=t^2\)으로 소거할 수 있는 형태가 하나 생기고, 정리하면 \(a^2+b^2+c^2+d^2=t^2-462\)입니다. 가능한 \(t\)의 범위는, 판별식들(\(t^2-169\cdot 4 \ge 0\)) 등등으로부터 나옵니다. 그렇게 구한 \(t\)의 최솟값을 넣으면 구하려는 최솟값이 나옵니다.

 

30. Find the determinant of the following matrix

\(\left(\binom{7i+5j}{j}\right)_{0\le i,j \le n}\).

더보기

j열이 i에 대한 j차식이 된다는 걸 알 수 있습니다. elementary row operation을 반복하다 보면, 결국 이걸 최고차항만 남길 수 있다는 사실을 알 수 있습니다. \(\left(\begin{smallmatrix}1 & 1 & 1\\7\cdot 1 & 7\cdot 2 & 7\cdot 3\\\frac{7^2}{2}\cdot 1^2 & \frac{7^2}{2}\cdot 2^2 & \frac{7^2}{2}\cdot 3^2\end{smallmatrix}\right)\) 이런 식으로 말이죠. 앞에 \(7^k\)들을 밖으로 빼고 다시 이항 계수 형태로 바꿔주면 Pascal matrix가 됩니다. (Symmetric form) 이 행렬의 determinant는 1입니다. 그러므로 구하려는 답이 \(7^{\frac{n(n+1)}{2}}\)라는 사실을 알 수 있습니다.

'수학' 카테고리의 다른 글

루트 2가 무리수임을 n가지 방법으로 증명해보기  (1) 2019.07.01

작년의 파이널 라운드 진출팀이었지만, 올해는 예선 121등, 한국 4등으로 대회를 마무리하게 되었습니다. ㅠㅠ

새벽이라 좀 정신 없긴 하지만, 데이터별 공략을 간단히 적어보도록 할게요!

(혹시 나중에 더 풀어보실 생각이 있으시다면 미리 spoiler alert!)

 

A. example

더보기

pdf 파일에 있는 데이터를 그대로 출력하면 16점이 나오지만, 4번 책이 포함되도록 순서를 잘 조정하면 이 데이터에서 얻을 수 있는 최대 점수인 21점을 받을 수 있습니다. 

 

B. read on

더보기

데이터가 매우 단순합니다! 겹치는 책도 없고, 도서관마다 책 수도 똑같고, 다른 것은 signup time 뿐입니다. 그러므로 signup time이 작은 순서대로 출력하면 가능한 최고점인 5,822,900점을 받을 수 있습니다.

 

C. incunabula

더보기

이 데이터는 shipping size가 터무니없이 커서, signup만 하면 모든 책을 챙길 수 있습니다. 적절한 기준에 따라 Greedy하게 library를 고르는 것만으로도 5,689,502점을 받을 수 있었습니다. 대부분의 최상위권 팀들도 이 근처의 점수를 받았습니다. 저희 팀이 쓴 기준은 (추가되는 점수)/(signup time)이었고, 다른 library를 추가할 때마다 추가되는 점수를 실질적으로 반영하여 다시 산정하였습니다. 이러한 update의 관리는 c++의 priority_queue 등으로 깔끔하게 처리할 수 있습니다.

 

D. tough choices

더보기

데이터 이름이 암시하듯, 사실 이 데이터는 두 library 중 하나를 고르는 선택의 연속입니다. 데이터를 잘 관찰해보면, 2k번 library와 2k+1번 library가 책을 하나씩 공유합니다. 그리고 이 책들은 그 둘에서만 등장하죠. 나머지 책들은 모두 세 번씩 등장합니다. 30000개의 library, 각각의 signup time 2, 그리고 deadline 30001. 이 수들이 갖는 의미는, library를 15000개 고를 수 있다는 것이고, 만약 만점을 받을 수 있다면, 그 데이터는 2k번 library와 2k+1번 library 중 정확히 하나만을 포함할 것입니다. 그래서 2k번 library와 2k+1번 library 중 책이 더 많은 쪽을 일단 골라봤습니다. 그 자체로는 만족할 만한 점수가 나오지 않았지만, 상태를 flip하며(2k번이 골라졌었다면 그걸 해제하고 2k+1번을, 반대쪽도 비슷하게) 더 좋은 쪽으로 선택하도록 했습니다. 그 결과 5,043,025점을 받았습니다. 참고로 이 문제의 이론상 만점은 65*78600=5,109,000점입니다. 최상위권 팀들 중에는 510만점을 넘긴 팀들이 몇 있었고, 이 팀들은 데이터의 특징으로부터 추가적인 관찰을 했습니다. 둘 중 하나를 고른다는 것을, p나 ~p 중 하나를 고른다는 걸로 생각하는 거죠. 그러면 이 문제는 78600-15000=63600개의 clause와 15000개의 variable로 이루어진 3-SAT 문제입니다. 3-SAT solver를 이용하면 510만점 이상의, 거의 만점에 가까운 점수를 얻을 수 있다고 합니다.

 

E. so many books

더보기

이 데이터가 최상위권과 상위권을 가르는 데이터가 되지 않았나 싶습니다. 정말 별 방법이 다 있었고, MCMF부터 이상한 hyper-parameter 조작까지(<-이게 저희 팀이었습니다) 다양한 방법과 다양한 점수가 있었습니다. 최상위권의 점수는 520만점 정도였지만 저희 팀은 이 데이터에서 5,095,422점을 내는 데 그쳤습니다. 이 부분이 가장 아쉽네요.

 

F. libraries of the world

더보기

이 데이터도 E만큼은 아니지만 팀별로 점수 차이가 조금 났었습니다. 넣을 수 있는 library의 수가 그렇게 많지 않다는 사실로부터 접근하면 여러 경우들을 잘 시도해보는 것으로 다수의 팀들이 5,348,248점을 받았습니다. 저희 팀은 5,317,660점을 받았네요.

 

이렇게 저희 팀은 26,968,350점을 받았습니다. 한국에서는 78-9 팀이 27,070,220점으로 가장 높은 점수를 받았고, 전체 순위도 38등이라 사실상 월드 파이널 진출이신 것 같습니다! 아직은 모르긴 하지만 미리 축하드리고, 4월 25일(한국 시간으로는 하루 뒤일 수도 있습니다만)에 있을 월드 파이널에도 많은 관심 가져주시면 좋겠습니다! 또, 앞으로도 한국 팀들이 더 선전하여 내년에는 더 많은 한국 팀을 월드 파이널에서 볼 수 있었으면 좋겠네요!

루트 2(\(\sqrt{2}\))가 무리수임을 처음 배우는 것은 중학교 3학년 때이다('19 기준). 그 증명을 기억하는가? 기본적으로는, 유리수라는 걸 가정하고, 어찌어찌하다가, 모순을 이끌어내서 유리수라는 가정이 틀렸다는 것으로부터 \(\sqrt{2}\)가 무리수임이 증명된다. 하지만 이 어찌어찌하는 방법은 사실 엄청 많다. 일반 사람들이 많이 아는 것만 해도 2~3가지는 되는 것 같고, 그 외에도 기상천외한 방법이 많이 있다. 그 수많은 방법들을 살펴보자. 그리고, 일반적인 \(\sqrt{n}\)(\(n\)은 완전제곱수가 아닌 자연수)에 대해 확장할 수 있는지 살펴보자.

 

본격적인 증명들을 소개하기 전에, 다음과 같은 사실들을 "믿도록" 하자.

 

I. \(\sqrt{2}\)는 실수이다.

II. 모든 유리수는 \(\frac{a}{b}\) (\(a,b\)는 서로소인 정수, \(b \neq 0\))의 꼴로 나타낼 수 있다.

III. 실수이면서 유리수가 아닌 수는 무리수이다.

IV. 배중률은 참이다.(\(p \vee \neg p\))

V. \(1 < \sqrt{2} < 2 \)이다.

 

1. 간단한 증명

\(\sqrt{2}\)가 유리수라 가정하자. 그렇다면 두 정수의 비율로 나타낼 수 있으므로, \(\sqrt{2}=\frac{a}{b}\) (\(a,b\)는 서로소인 정수, \(b \neq 0\))로 나타낼 수 있다. 양변에 \(b\)를 곱하고 제곱하면

$$2b^2 = a^2 \qquad \cdots (1)$$

따라서 \(a^2\)은 짝수이다. 홀수의 제곱은 홀수, 짝수의 제곱은 짝수이므로 \(a\)가 짝수라는 결론이 나온다. \(a=2c\)라 두자. (1)에 대입하면

$$2b^2 = 4c^2, b^2 = 2c^2$$

그러므로 위와 비슷하게 \(b\)가 짝수라는 결론이 나온다. 하지만 맨 처음에 우리는 \(a,b\)가 서로소라고 했다. 그런데 \(a,b\)가 모두 2로 나누어떨어지므로 이는 모순이다. 그러므로 \(\sqrt{2}\)는 무리수이다. 증명 끝!

 

※ 확장

더보기

일단은 \(n\)을 \(k^2\times A\) (\(A\)는 무승수) 꼴로 나타낸다. 무승수란, 1을 제외한 어떤 완전제곱수로도 나누어떨어지지 않는 수이다. 이 때 \(A\)의 소인수 하나를 잡아서 위와 같은 논리를 펼칠 수 있다. \(\sqrt{A}\)가 무리수임을 보인다면, \(\sqrt{n}=k\sqrt{A}\)가 무리수라는 것도 자연스럽게 증명된다!

 

◎ 앞으로는 확장 부분을 쓸 때, 별다른 말이 없으면, 위에서와 같이 구한 무승수 \(A\)에 대해서만 서술한다.

 

2. 소인수분해의 유일성을 이용한 증명

\(\sqrt{2}\)가 유리수라 가정하자. 그렇다면 두 정수의 비율로 나타낼 수 있으므로, \(\sqrt{2}=\frac{a}{b}\) (\(a,b\)는 자연수)로 나타낼 수 있다. 양변에 \(b\)를 곱하고 제곱하면

$$2b^2 = a^2 \qquad \cdots (1)$$

한편 \(a\)의 소인수분해에서 2의 지수를 \(e_a\), \(b\)의 소인수분해에서 2의 지수를 \(e_b\)라 하면, (1)의 좌변의 소인수분해에서 2의 지수는 \(2e_b+1\)로 홀수인데, 우변의 소인수분해에서 2의 지수는 \(2e_a\)로 짝수이다. 그런데 두 수는 같으므로, 소인수분해의 유일성에 의해 양변의 2의 지수는 같아야 한다. 홀수와 짝수는 같을 수 없으므로 이는 모순. 그러므로 \(\sqrt{2}\)는 무리수이다.

 

※ 확장

마찬가지로 \(A\)의 소인수 하나를 잡아서 같은 논리를 펼칠 수 있다.

 

3. 무한강하법을 이용한 증명 1

\(\sqrt{2}\)가 유리수라 가정하자. 그렇다면 두 정수의 비율로 나타낼 수 있으므로, \(\sqrt{2}=\frac{a}{b}\) (\(a,b\)는 자연수)로 나타낼 수 있다.

\(a_0=a, b_0=b\)로 두자. 1번에서와 같은 논리로 \(a_0\)는 짝수이다. \(b_1=\frac{a_0}{2}, a_1=b_0\)로 놓으면

$$\frac{a_1}{b_1} = \frac{2b_0}{a_0} = \frac{2}{\sqrt{2}} = \sqrt{2}$$

이다. 이제 우리는 하나의 자연수 쌍 \((a_0, b_0)\)로부터 새로운(그리고 여전히 비율이 \(\sqrt{2}\)인) 자연수 쌍 \((a_1, b_1)\)을 얻어냈다. 게다가

$$0 < a_1 + b_1 = \frac{a_0}{2} + b_0 < a_0 + b_0$$

이다. 이 과정을 반복하면 \((a_1, b_1)\)으로부터 또 다른 자연수의 쌍 \((a_2, b_2)\)를 얻어낼 수 있고, 이런 식으로 두 수의 합이 계속해서 줄어드는 자연수 쌍을 무한히 많이 만들어낼 수 있다. 하지만 초기값인 \(a_0 + b_0 = a+b\)는 유한한 값이므로 모순이다. 그러므로 \(\sqrt{2}\)는 무리수이다.

 

※ 확장

더보기

\(A\)의 임의의 소인수 \(p\)에 대해 \(a_0\)가 \(p\)의 배수라는 사실을 통해 \(a_0\)가 \(A\)의 배수라는 결론이 나오게 된다. 그렇다면 반복되는 과정에 있는 모든 \(2\)를 \(A\)로 바꿔줄 수 있다.

 

♤ comment

3번까지의 증명이, 간단한 편이고, 설명하기에도 어렵지 않고, 잘 알려진 증명일 것 같다. 그리고 눈치챘을 수도 있지만 세 증명이 매우 유사한 형태를 띄고 있다. 이제부터 나오는 증명들은, 위의 것들보다는 조금 덜 알려진 증명들이 될 것이다.

 

4. 무한강하법을 이용한 증명 2

\(\sqrt{2}\)가 유리수라 가정하자. 그렇다면 두 정수의 비율로 나타낼 수 있으므로, \(\sqrt{2}=\frac{a}{b}\) (\(a,b\)는 자연수)로 나타낼 수 있다.

\(a_0=a, b_0=b\)로 두자. \(\sqrt{2}-1 = \frac{a_0-b_0}{b_0}, \frac{1}{\sqrt{2}-1} = \frac{b_0}{a_0-b_0}\)인데, \((\sqrt{2}-1)(\sqrt{2}+1)=1\)이므로 \(\frac{1}{\sqrt{2}-1} = \sqrt{2} + 1\)이다. 따라서

$$\sqrt{2}+1 = \frac{b_0}{a_0-b_0}, \sqrt{2} = \frac{2b_0-a_0}{a_0-b_0}$$

한편, \(1 < \sqrt{2} < 2\)이므로, \(a_0 < b_0 < 2a_0\)이다. 이로부터 \(0 < a_0-b_0 < a_0, 0 < 2b_0-a_0 < b_0\)가 유도된다. \(a_1 = a_0-b_0, b_1 = 2b_0-a_0\)라 놓는다. 이제 우리는 하나의 자연수 쌍 \((a_0, b_0)\)로부터 새로운(그리고 여전히 비율이 \(\sqrt{2}\)인) 자연수 쌍 \((a_1, b_1)\)을 얻어냈다. 게다가 \(0 < a_1 < a_0, 0 < b_1 < b_0\)이다. 이 과정은 무한히 반복 가능하다. 하지만 처음의 \(a_0 = a\)는 유한한 값이므로 이는 모순이다. 그러므로 \(\sqrt{2}\)는 무리수이다.

 

※ 확장

더보기

원래의 수 \(n\)에 대해서 바로 적용해보도록 하자!

\(n=k^2 + t\) (단, \(k^2 < n < (k+1)^2\))이라 하자. 위와 비슷한 과정으로 \(\sqrt{k^2 + t} = \frac{(k^2+t)a_0-kb_0}{b_0-ka_0}\)를 유도할 수 있다. 이제 해야 할 일은, 이렇게 나온 분모와 분자가 각각 \((0,a_0), (0,b_0)\) 안에 들어오는가를 확인하는 것이다. 그리고 그것을 확인하기 위해 사용할 수 있는 식은 \(ka_0 < b_0 < (k+1)a_0\)이다.

분모에 대한 식 \(0 < b_0-ka_0 < a_0\)은 바로 유도된다. 분자가 문제인데, 양쪽 부등식을 차근차근 해 보자.

$$(k^2+t)a_0-kb_0 > 0 \Longleftrightarrow \frac{b_0}{a_0} < \frac{k^2+t}{k} \Longleftrightarrow k^2+t < \left(\frac{k^2+t}{k}\right)^2 \Longleftrightarrow k^2 < k^2 + t$$

$$(k^2+t)a_0-kb_0 < b_0 \Longleftrightarrow \frac{b_0}{a_0} > \frac{k^2+t}{k+1} \Longleftrightarrow k^2+t > \left(\frac{k^2+t}{k+1}\right)^2 \Longleftrightarrow (k+1)^2 > k^2 + t$$

따라서 \(0 < (k^2+t)a_0-kb_0 < b_0\)임이 증명되었다! 이제 같은 논리를 적용할 수 있게 되었다.

 

♤ comment

처음 이 증명을 접하고, 식을 정말 아름답게 다루는 풀이라고 생각했다. 계수 같은 것들도 절묘하게 맞아 떨어졌는데, 확장을 해 보니까 왜 실제로 맞아 떨어지는지를 확인해볼 수 있었다. 확장 부분에서는, 처음엔 \(ka_0 < b_0 < (k+1)a_0\) 이외에 다른 조건이 필요할 거라고 생각했는데, 신기하게도 필요가 없었다.

 

5. 최대공약수 정리를 이용한 풀이

일단 최대공약수 정리가 무엇인지부터 보도록 하자.

"\(\gcd(a,b)=d\)라 하면, \(ax+by=d\)인 정수 \(x,y\)가 존재한다."

자세한 내용 및 증명은 https://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity을 참고하면 될 것 같다.

 

본 증명

\(\sqrt{2}\)가 유리수라 가정하자. 그렇다면 두 정수의 비율로 나타낼 수 있으므로, \(\sqrt{2}=\frac{a}{b}\) (\(a,b\)는 서로소인 정수, \(b \neq 0\))로 나타낼 수 있다. 이 때 최대공약수 정리에 의해

$$ax+by=1 \qquad \cdots (1)$$

인 두 정수 \(x,y\)가 존재한다. (1)의 양변에 \(\sqrt{2}\)를 곱하면

$$\sqrt{2}ax+\sqrt{2}by=\sqrt{2}$$

이다. 한편 \(\sqrt{2}b = a, \sqrt{2}a = 2b\)로부터

$$2bx+ay=\sqrt{2}$$

이다. 좌변은 정수인데 우변은 1보다 크고 2보다 작으므로 정수가 아니다. 이는 모순이다. 그러므로 \(\sqrt{2}\)는 무리수이다.

 

※ 확장

더보기

이것도 원래의 수 \(n\)에 대해서 바로 적용할 수 있다. \(2\)를 \(n\)으로 고쳐도 정확히 성립한다!

'수학' 카테고리의 다른 글

AIMath Quiz 후기 및 문제 풀이  (1) 2020.08.26

+ Recent posts