[Codeforces] Codeforces Round 1031 (Div. 2) 후기
지난 글 이후로 겨울 동안 코드포스를 조금씩 했었는데 민트를 찍자마자 다시 떨어졌다.
그 뒤로 더 떨어질 것 같다는 생각에 한동안 하지 않았다.
막연하게 코드포스 블루를 언제 다시 도전할지 생각만 하고 있을 때쯤 이번 라운드가 18시 05분 시작이었다.
종강도 했고 잃어버린 감각을 되찾다는 느낌으로 2솔을 목표로 이번 라운드에 참가했다.
Contest 링크 : Codeforces Round 1031 (Div. 2)
A. Shashliks
한 줄에 $k, a, b, x, y$가 주어진다.
두가지 요리가 있는데 첫번째 요리는 온도가 $a$ 이상일 때 요리할 수 있고 온도를 $x$만큼 감소시킨다.
두번째 요리는 온도가 $b$ 이상일 때 요리할 수 있고 온도를 $y$만큼 감소시킨다.
초기 온도가 $k$도일 때 요리할 수 있는 최대 횟수를 구하는 문제이다.
요리를 최대한 많이 해야 하므로 온도 감소가 적은 요리를 더 많이 하면 된다.
따라서 $x$와 $y$를 비교하고 온도 감소가 더 적은 쪽의 요리를 최대한 많이 만든 후 남은 온도로 다른 요리를 하면 된다.
이때 반복해서 빼기로 연산하면 TLE가 날 수 있기 때문에 나누기로 횟수를 세어주었다.
코드 보기
1 |
|
B. Good Start
$w * h$ 크기의 직사각형과 $a * b$ 크기의 직사각형이 주어지고 $a * b$ 직사각형 2개의 좌표가 주어진다.
$a * b$ 직사각형들로 $w * h$ 직사각형을 빈틈없이 덮어야 하는데 $a * b$ 직사각형을 회전시키면 안되고, 다른 $a * b$ 직사각형과 겹치게 놓으면 안된다.
이미 놓여진 2개의 $a * b$ 직사각형의 위치는 고정일 때 $a * b$ 직사각형을 더 추가해서 $w * h$를 덮을 수 있는지 판단하는 문제이다.
아래 그림을 보면 빨간색 직사각형이 이미 놓여진 $a * b$ 직사각형이고 여러 개를 더 추가하여 $w * h$ 직사각형을 다 덮었다.
하지만 이 경우에는 어떠한 방법으로도 덮을 수 있는 방법이 없다.
$w * h$ 직사각형을 다 덮기 위해서는 두 직사각형 사이의 간격이 변의 길이의 배수가 되어야 한다.
회전할 수 없기 때문에 두 직사각형의 $x$좌표 차이가 $a$의 배수이거나 $y$좌표 차이가 $b$의 배수여야 한다.
만약 둘다 배수가 아니면 덮을 수 없지만 하나라도 맞으면 덮을 수 있다.
다만 한 방향으로 좌표 차이가 0일 때는 다른 쪽이 배수이어야 덮을 수 있다.
그래서 두 직사각형 위치에 따라 경우의 수를 나누어 배수 판별을 해주면 된다.
코드 보기
1 |
|
4번 WA가 나왔는데 else if에서 조건문을 보면 괄호가 하나 비어 있어서 틀린 것이다.
저 부분을 눈치채지 못해서 계속해서 WA가 나와 맞왜틀을 외치다가 겨우 발견했다.
그런데 (y2 - (y1 + b)) % b
는 사실 (y2 - y1) % b
와 동일한 식이다.
긴장해서 그런지 복잡하게 생각해서 앞의 식으로 사용했는데 라운드 끝나고 에디토티얼을 보면서 복잡하게 생각했다는 것을 깨달았다.
처음부터 저렇게 조건문을 구성했다면 WA가 4번 나오지 않았을 것이다…
C. Smilo and Minecraft
$n * m$크기에 빈 땅, 돌, 금이 있다.
빈 땅에 TNT을 놓아 터트릴 수 있는데 TNT를 중심으로 한 변의 길이가 $2K - 1$인 정사각형 내부에 있는 모든 곳을 빈 땅으로 만들어 버린다.
그리고 폭발의 범위와 맞닿아 있는 부분의 금을 가져갈 수 있다.
아래 예시를 보면 TNT의 폭발력이 2일 때 폭발이 만드는 정사각형 길이는 3이 되고 두번째에서 그림에서 이와 맞닿아 있는 부분의 금 2개를 가져갈 수 있다.
이후 TNT를 한번 더 터트려 남은 금 2개도 가져갈 수 있다.
TNT를 여러번 터트릴 수 있을 때 최대한 많이 금을 몇 개 가져가야 하는지 구해야 한다.
처음에 든 생각은 백트래킹으로는 절대 풀 수가 없는데 어떻게 접근할지 의문이 들었다.
그래서 저 사진 처럼 직사각형의 꼭짓점 근처부터 터트리면 최대한 손실 없이 가져갈 수 있다고 생각했는데 꼭짓점 쪽에 빈 칸이 없으면 이 방법도 무용지물이다.
여기서 떠오른 것은 처음 폭발을 일으키면 외곽부분의 금을 모두 회수하기 때문에 그 다음부터는 외곽부분이 폭발 범위에 있어도 괜찮다는 것이다.
즉 처음 폭발 때 금을 최소한으로 잃으면 남아있는 금을 모두 가져갈 수 있다.
따라서 빈 칸에 TNT를 한 번씩 놓아보면서 몇 개 잃는 지 계산한 후 전체 금의 개수 - 최소로 잃는 금의 개수
를 계산하면 된다.
$k$의 범위가 $(1 \leq k \leq 500)$이므로 매 위치마다 주변을 탐색하긴 힘들기 때문에 2차원 누적합을 활용해 미리 금의 개수를 미리 저장해 놓고 매 위치마다 계산하면 된다.
정리해보면
- 2차원 누적합으로 금의 개수를 저장한다.
- 빈칸을 돌면서 TNT로 잃는 금의 최솟값을 계산한다.
- 전체 금의 개수에서 최솟값을 빼어 최대로 얻을 수 있는 금의 개수를 계산한다.
코드 보기
1 |
|
평소 배열 저장은 0-base
로 하고 누적합은 1-base
로 하다보니 이를 변환할 때 머리가 꼬여서 제대로 계산이 되지 않았다.
이 부분에서 디버깅만 30분 걸린 것 같다. 그래서 시간이 점점 줄어들수록 초조해졌는데 다행히 풀 수 있었다.
D. Cheater , E. From Kazan with Love , F. Two Arrays
C번을 푼 시점에서 8분 정도 남아 있어서 라운드를 마무리 했다.
후기
원래 목표가 2문제였는데 3문제를 풀었다.
B번에서 WA만 4번 나올 때 오늘 라운드도 점수가 떨어지겠다 싶었는데 다행히 C번을 풀어 점수가 올랐다.
B번에서 괄호만 잘 봤으면 점수가 훨씬 더 좋았을 것 같다.
그리고 이번 라운드를 통해 무사히 민트를 복구했다.