일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- BAPC
- Z algorithm
- 기하 알고리즘
- BAPC 2018
- Manacher's algorithm
- Implementation
- boj
- ICPC 연습
- convex hull
- 8192
- JOI
- IOI 2013
- POI Solution
- 세그먼트 트리
- 코드포스
- acmicpc.net
- 백준 온라인 저지
- 그래프
- Z 알고리즘
- Manacher 알고리즘
- 2794
- Divide and conquer optimization
- 볼록 껍질
- 구현
- 14179
- 기하
- 컨벡스 헐
- JOI 2018
- codeforces
- DP
- Today
- Total
프로그래밍 연습장
ICPC Yokohama Regional 2018 본문
오랜만에 셋을 돌았다. rkm0959와 2인 팀으로 돌았다. 문제들이 대체적으로 마음에 들었지만, 그냥 마음이 아프다. 업솔빙한 문제들의 풀이를 간략하게 정리해 본다.
A Digits Are Not Just Characters
더러운 문제다. 생략.
B Arithmetic Progressions
보자마자 "아 ㅋ 10초 컷이네 ㅋ" 생각해서 키보드를 잡자마자 그 풀이가 O(N3)임을 깨달았다. 20초 더 생각하니 O(N2)더라. 내 앞에 있던 놈도 그런 거 같았다.
a1과 a2로 쓸 두 개의 수를 정하면 만들 등차수열이 정해지고, 등차수열이 정해졌다면, 주어진 수열에서 등차수열을 몇 번째까지 뽑을 수 있는가는 쉬운 연습문제이다. 이를 단순히 구현하면 O(N3)이다. 여기서, DP처럼 같은 (a1,a2)의 쌍을 한 번만 세도록 처리해주자. 이 부분을 잘 이해하기 힘들다면, DP같이 처리한다고 생각하자. dp(i,j)를, a1=vi, a2=vj로 두었을 때 만들 수 있는 등차수열의 길이로 두자. vk=a3=a2+(a2−a1)이 되는 k가 존재한다면 dp(i,j)=1+dp(j,k)가 된다. 이제, 중복된 (i,j)에 대해 한 번만 셀 수 있으며, O(N2)의 시간 복잡도에 문제를 해결할 수 있다.
C Emergency Evacuation
다른 사람의 방해가 없을 때, 각 사람이 출입문까지 도달하는 최소 시간(i.e. 거리)는 쉽게 알 수 있다. ti=|W−xi|+|H−yi|+2일 것이다. 그냥, 각 사람이 그 시점에 출입문 앞에 뿅 하고 나타나서 줄을 선다고 생각하자. 그리고는 1초에 한 명씩만 내보내준다고 생각하자. 이 경우, 문제의 풀이는 자명하다. t1,...,tN을 오름차순으로 정렬하고, 각 i=2,...,N에 대해 ti=max를 수행하면 된다. 이 때 마지막 사람은 연산이 끝난 후 t_N의 시간에 나가게 될 것이다.
그렇지만, 원래 문제에서는 제약사항이 좀 더 많았으니 답이 좀 더 크지 않을까? 사실은, 원래 문제의 답도 같다! 이 사실은, 거꾸로 사람들을 들여보내주는 상황을 생각하면 쉽게 납득이 된다. 사람들을 출입문에서 1초에 한 명씩 차례대로 들여보내야 하는 상황이라면, 가장 자리가 멀리 있는 사람부터 들여보내야 모두가 앉게 되는 시간이 빨라진다.
D Shortest Common Non-Subsequence
LCS 문제와 비슷해 보인다. 풀이도 LCS와 비슷할 것처럼 생겼다. dp[i][j]를, A[i...]과 B[j...]의 Shortest common non-subsequence의 길이라고 하자. 종이에 조금 끄적여보면 그럴듯한 점화식이 나온다. zero_S (i)를, S[i...]에 등장하는 첫 0의 위치로, one_S(i)를 S[i...]에 등장하는 첫 1의 위치로 두자. 그러면,
dp[i][j] = min(dp[zero_A(i)+1][zero_B(j)+1], dp[one_A(i)+1][one_B(j)+1])
이 된다. 유의. 만약 입력으로 빈 문자열 두 개가 들어온다면 답은 길이 1인 문자열 0이다. 그러니, (0-indexed notation으로) dp[N][M] \neq 0이고, 오히려 dp[N+1][M+1] = 0이다. A[N+1...]라는 말은, 빈 문자열과는 뉘앙스가 다른, "길이가 -1인 문자열"이며, A[N...]과는 오히려 구분해야 하는 것이다. 이 점을 "세심하게" 고려해서 세심한 DP식을 세워야 한다. 나는 이 문제를 풀면서 불길한 예감을 느끼면서도, 이 진실을 외면하고 사전순 최소로 복원해야 한다는 조건 탓을 했다. 고통스럽더라도 진실을 마주할 수 있는 용기가 필요하다.
E Eulerian Flight Tour
왜 맞지?
*
F Fair Chocolate-Cutting
*
G What Goes Up Must Come Down
수열에서 가장 작은 수는 가장 왼쪽 또는 가장 오른쪽으로 (언젠가는) 보내야 한다. 그리고, 그렇게 최소원소를 양쪽 끝 중 하나로 보내면, 남은 수열에 대한 문제는 완전히 독립적인 다른 문제이다. 그러면, 가장 작은 수를 왼쪽으로 보내야 할까, 오른쪽으로 보내야 할까? 그냥 가까운 쪽으로 보내면 장땡이다. 어느 쪽으로 보냈든 이후의 문제와는 별 상관이 없으니.
구현할 때에는 우선순위 큐, 펜윅 트리, 세그먼트 트리 등을 쓰면 된다.
H Four-Coloring
구사과님의 오색 정리 포스트를 참조한다. 이 글에서 오색 정리의 첫번째 증명을 간략히 리뷰한다:
- 수학적 귀납법을 사용한다. 기저는 물론 참이다.
- 증명은 무방향 평면 그래프에는 차수가 5 이하인 정점이 존재한다는 보조정리에서 시작한다. 이건 오일러 방정식에 의해 유도되고, 그냥 믿자, 지금은.
- 그러한 정점 v를 하나 고르고, 그래프에서 v를 제외한 정점들을 모두 칠해본다. 귀납 가정에 의해 물론 가능하다.
- v의 최대 다섯개인 이웃을 u_1, \cdots, u_5로 각도 순서대로 이름붙이자. 이 이웃들은 어떻게든 칠해져 있을 것이다.
- 이웃들을 칠할 때 1번 색에서 5번 색까지를 모두 사용하지 않았다면 운이 좋은 것이다. 이웃들을 칠할 때 사용하지 않았던 색 하나를 칠하면 된다.
- 운이 나쁘게 이웃들에게 쓰고 남은 색이 없다면 어떻게 할까? 그래도 괜찮다. 이웃들의 색을 적절히 재배치해서 여유분을 하나 남길 수 있다.
- u_1, \cdots, u_5에 칠한 색을 차례로 c_1, \cdots, c_5라고 하자. 원래 그래프에서 c_1, c_3의 색으로 칠한 정점만을 남겼을 때, u_1과 u_3이 연결되어 있지 않다면, 그래도 운이 좋은 편이다. u_1의 색을 c_3으로 바꾸고, u_1 주변에 있는 c_3으로 칠해진 정점들의 색을 c_1으로 바꾸고, 그 주변의 정점들의 색을 c_3으로 바꾸고, 쭉 해 준다. u_1과 u_3이 연결되지 않은 경우이므로 이 과정은 적당히 굴러가다 끝난다.
- 위 과정이 끝나면 u_1의 색이 c_3이 되면서도 올바르게 색을 칠하게 되었고, 그러면 v에는 c_1을 칠할 수 있게 되었다! 예~!
- 정말 운이 나쁘다면..? ㅜㅜ 그러면 어쩔 수 없다. 대신, u_2와 u_4를 붙잡고 같은 짓을 해 보자.
- 증명의 핵심은, 두 과정이 모두 실패할 수는 없다는 것이다. 즉, c_2, c_4의 색으로 칠한 정점만을 남겼을 때, u_2와 u_4는 무조건 분리되어 있다는 것이다!
- 왜? 그래프가 평면 그래프이기 때문이다! 두 과정이 모두 실패했다면, c_1, c_3으로 칠해진 정점으로 u_1과 u_3을 이은 경로와, c_2, c_4로 칠해진 정점으로 u_2와 u_4를 이은 경로가 모두 존재한다. 그런데, 두 경로는 교차한다! 그러면 그 교차점은 c_1, c_3 중 하나로 칠해져야 하는 것인가, 아니면 c_2, c_4 중 하나로 칠해져야 하는 것인가? 여기서 두 과정이 모두 실패하면 모순이고, 두 과정 중 하나는 성공함을 보였다.
위 증명은 실제적으로 일반적인 평면 그래프를 오색으로 색칠하는 방법을 제시한다. 그런데, 문제에서 제시하는 평면 그래프는 더 강력한 성질을 가진다. 얘는 무조건 차수가 4 이하인 정점을 하나 가진다. 바로 그래프의 가장 오른쪽 아래에 있는 점이다. 얘를 가지고 위의 과정을 그대로 따라가면 입력으로 주어지는 형태의 그래프를 4개의 색으로 칠할 수 있다!!!(증명의 마지막 부분에서 u_5는 전혀 써먹지 않았다는 점을 확인하라.) 시간 복잡도는 O(N^2)로 별 문제없다.
I Ranks
*
J Colorful Tree
색안경을 끼고 c번 색의 관점에서만 문제를 보자. 사실 이 문제는 다음 세 가지 연산을 처리하는 문제와 같다.
- 정점 v에 표시를 한다.
- 정점 v에 한 표시를 지운다.
- 표시한 정점들을 모두 잇기 위해 필요한 최소 간선 수를 구한다.
3번 질의의 답은, 초기에는 0이고, 1번과 2번 질의에 따라 증가하거나 감소할 것이다. 3번 질의의 답을 질의가 들어올 때 계산하지 않고 1번과 2번 질의가 들어올 때 변화량을 잘 계산해줘서 슝슝 더하고 빼 줄 수 있지 않을까?
트리를 루트에 매달고, 가만히 생각해 보자. 만약 지금 매달아놓은 트리의 정점 몇 개에 표시가 되어 있다고 생각해 보자. 이들을 모두 잇는 간선들은 또 다른 트리처럼 생겼을 것이다. 얼핏 보면, 이들 전체의 LCA (l이라 두자) 에다가 표시한 정점들을 주렁주렁 매달아둔 것처럼 생겼다. 즉, 이들을 모두 잇는 간선들은 LCA에서 각 정점으로 가는 경로들의 합집합이다. 이제, 여기에 새로운 정점 x를 추가하면 3번 질의의 답은 얼마나 증가할까?
만약 새로 추가한 정점 x가 l의 자손이 아니라면, 정확히 x와 l 사이의 거리만큼 답이 증가한다. 만약... l의 자손이라면? 고른 정점들을 모두 잇기 위해서는 x에서 l로 가는 경로만큼을 결국은 추가해야 할 건데, 기존에 골라둔 간선들과 얼마나 겹치는지를 우리는 모른다. x에서 가장 가까운 '가지'를 찾아서, 그 가지까지의 거리를 재야겠다. 그러기 위해서 우리는 정점들에 DFS 순서를 매겨두고, 고른 정점들 중에서 x와 DFS 순서가 가까운 두 정점(왼쪽에서 하나, 오른쪽에서 하나)만 잡아서 실제 거리를 재보면 된다. 그 중 짧은 길이만큼을 3번 질의의 답에 더해준다.
삭제는 반대로 하면 된다. (진짜로!)
K Sixth Sense
편의상, 상대편이 가진 배열을 a, 내가 가진 배열을 b라고 하자. 사전순으로 최대인 배치를 출력하라는 조건이 없을 때에는 잘 알려진 그리디 알고리즘으로 풀 수 있다.
- a와 b를 정렬한다.
- a의 원소를 a[1], ..., a[N] 순서대로 보면서, b에 a[i]보다 큰 원소가 존재하면 사용해서 점수를 올린다.
(물론, a를 정렬하지 않아도 문제없다.)
이 방법은 O(N)이다.
사전순으로 최대인 배치를 출력해야 하는 게 좀 짜증난다. 첫번째 판에는 뭘 내야 할까? 첫번째 판에서 이길 수 있으면 언제나 이기는 게 이득이다. 사전순 최대를 고려하지 않는다면 a[1] 초과의 첫번째 수를 쓰는 게 이득인데, 이제 좀 애매해진다. "첫번째 수로 얘를 써도 여전히 최적해가 되는지"로 parametric search를 돌리자. 첫번째 수 후보를 \log N번 고려하게 되므로, O(N \log N)에 첫번째 원소를 결정할 수 있다. 물론, 전체 시간 복잡도는 O(N^2 \log N).
'문제 해결' 카테고리의 다른 글
2020 선린인터넷고등학교 정보 경시대회 (6) | 2020.09.07 |
---|---|
190720 팀 연습: BAPC 2018 (2) | 2019.07.21 |
180519 동아리 연습 문제 풀이 (0) | 2018.05.19 |
180428 동아리 연습 문제 풀이 (1) | 2018.04.28 |
180421 동아리 연습 문제 풀이 (1) | 2018.04.21 |