pstopia Notes for Problem Solving Contest

[Hard] Reflections

Topcoder SRM 513 Div1

Problem

mirrorX 를 이용할 때는 y, z 좌표가 바뀌지 않는다. mirrorY 와 mirrorZ 에 대해서도 같은 식으로 된다. 따라서 x좌표의 이동, y좌표의 이동, z좌표의 이동을 각각 따로 볼 수 있다. solve(mirrors[], target) 같은 함수를 만들어서 x좌표, y좌표, z좌표에 대해 각각 불러주면 된다. 몇가지 더 관찰해야하는 성질이 있다. - 각 거울은 최대 한번만 사용하는 것이 좋다. - 내가 현재 좌표a에 있고, 좌표m에 있는 mirror를 이용한다면 2*m - a 로 이동하게 된다. 내가 거울의 왼쪽에 있든 오른쪽에 있든 상관없다. - normal move 를 한 후 mirror 를 이용하는 것과 그 반대는 차이가 없다. mirror 가 한 개 있을 때를 먼저 증명한 후, 그걸 여러개 이용하는 것을 생각하면 된다. 이 성질들을 이용해 풀이를 만들어보자. m개의 mirror 가 X[1], X[2], ..., X[m] 에 위치해있다고 해보자. 이 mirror 들을 전부 다 사용하고 마지막에 normal move 를 수행해서 0에서 출발해 목적지까지 최소비용으로 이동하려고 한다. mirror 를 이용하는 순서를 a1, a2, ..., am 이라고 하면 처음 mirror 를 이용하면 2*X[a1] - 0 그다음 mirror 를 이용하면 2*X[a2] - (2*X[a1] - 0) 그다음 mirror 를 이용하면 2*X[a3] - (2*X[a2] - (2*X[a1] - 0)) ... 이런 식이 된다. mirror 를 전부 이용하고 나면 좌표는 2*(X[a]+X[b]+X[c]+...) - 2*(X[p]+X[q]+X[r]+...) 와 같은 형태가 된다. 그리고 (+항에 있는 X의 개수) - (-항에 있는 X의 개수) == 0 or 1 을 만족해야 한다. 그 후에 마지막으로 normal move를 사용하면 총 비용은 |(2*(X[a]+X[b]+X[c]+...)-2*(X[p]+X[q]+X[r]+...)) - (목적지 좌표)| + (+항에 있는 X의 개수) + (-항에 있는 X의 개수) 가 된다. k <= (n+1)/2 인 k에 대해, mirror 를 k개 고르는 경우를 모두 순회하면서 X의 합을 저장해 bucket[k] 에 다 넣고 정렬해둔다. bucket[k] 의 원소 하나를 sumA 라고 하고 이걸 +항의 합이라고 하자. -항의 합 sumB 는 bucket[k] 나 bucket[k-1] 에서 고를 수 있고 |(2*sumA - 2*sumB) - (목적지 좌표)| 이 최소가 되도록 골라야 한다. 이것을 이분검색으로 찾든 two pointer 로 찾든 하면 된다. sumA를 구성하는 집합과 sumB를 구성하는 집합이 서로소가 아닐 수도 있지 않냐! 라고 생각할지도 모르지만 만약 두 집합에 공통인 원소가 존재하면 그 때의 답은 무조건 최적이 될 수 없다. 따라서 그냥 저렇게 돌려도 상관없다. 전체 시간복잡도는 O(2^n * n)