2016. 7. 28. 00:06ㆍ알고리즘 문제풀기/각종 OJ 풀이
1. 3000번 버스
2. 과제 안 내신 분..?
배열 a
를 만든다.
x
라는 숫자를 읽고 나면 a[x]
에 체크한다.
이후 a
배열을 순회하며 체크되지 않은 x
를 찾아 답을 출력한다.
문제 자체는 단순하지만, counting sort 등의 아이디어와 밀접하게 닿아있다.
3. Amalgamated Artichokes
영어라서 어려워 보이지만 읽어보면 쉬운 문제이다.
아래의 리빙포인트를 꼭 참고하자.
리빙포인트
문제가 영어라서 읽기 귀찮을 땐, 2~3번째 문단부터 읽어보자.
라고 하자.
인 중 의 최댓값을 구하는 문제이다.
의 식이 복잡하기 때문에, 그냥 하면 실수 오차가 생기고 저 식을 잘 분석해서 정확한 값을 구하는 문제처럼 보일 수 있다.
하지만 그런 걸 요구하는 문제는 아니다.
어떤 i
에 대해, 가 최대가 되는 j
는, 1~(i-1)
중 가 가장 큰 j
일 것이다. 이러한 j
를 일일이 찾으면 풀이가 된다.
에 해결하기 위해서는 어떻게 해야 할까? i
를 1
부터 n
까지 돌며, 를 유지하고 있다면, 그 값과 를 비교하여 답을 갱신해주면 된다.
자세한 방법은 소스 코드를 참고하자.
소스 링크
4. 카드
STL 연습문제이다. long long
을 써야 하는 것을 잊지 말자.
방법 1.
들어온 값들을 정렬한다.
정렬된 상태에서, 서로 같은 값들은 인접해있으므로, 배열을 돌며 그 길이를 잘 구해주면 된다.
소스 링크
방법 2.
std::map
을 사용한다.
만약 이 문제를 2번과 같은 방법으로 푼다고 하자. 즉, x
라는 값이 들어오면 a[x]
에 1을 더해주고, 마지막에 전체를 순회하며 값을 검사한다고 하자.
이 방법의 문제는, 값의 범위가 너무 크다는 것이다. 값의 범위가 크기 때문에 모두 커버 가능할만큼 큰 배열을 잡는 것과 모든 값을 순회하는 것이 불가능하다.
std::map
은 이러한 문제를 해결해준다. 사용방법은 직접 검색해보거나 소스를 보면서 익혀보자.
소스 링크
5. 두 용액
값을 정렬했다고 하자. 정렬하는 부분을 제외하고 봤을 때 시간복잡도가 다른 두 가지 방법이 있을 수 있겠다.
1. 방법
배열의 각각의 원소 x
에 대해, -x
와 가장 가까운 원소를 찾으면 된다.
즉, -x
이하인 것중 가장 큰 원소와, -x
이상인 것중 가장 작은 원소를 찾으면 될 것이다.
각각을 이분탐색으로 찾을 수 있다.
개의 원소를 보며 의 시간이 걸리는 이분탐색을 수행하므로 총 시간복잡도는 .
2. 방법
위의 방법과 비슷하다.
만약 x
에 대해 가장 가까운 원소 두 개를 찾았다고 하자.
x
가 증가하면, 그 가장 가까운 원소들은 감소할 것이다.
즉, 가장 가까운 원소의 위치를 나타내는 값을 가지고 있고, x
를 점점 증가시킴과 동시에 그 값을 필요한 만큼 줄인다.
x
는 1에서 N까지, ‘가장 가까운 원소의 위치’는 N에서 1까지 감소할 것이고, 어떤 두 원소가 비교되는 횟수 또한 N에 비례한다.
따라서 전체 시간복잡도는 .
6. 세 용액
이므로, 이 포함된 시간복잡도도 생각 가능하다.
어떤 두 용액 x
, y
를 잡았다면, -(x+y)
에 가장 가까운 원소를 하나 찾으면 될 것이다.
이 때 주의할 점은, 그 원소들이 x
나 y
와 겹치지 말아야 한다는 것이다.
해당 원소를 이분탐색으로 찾으면 이다.
x
를 고정시키고, y
를 증가시킴에 따라 가장 가까운 원소를 찾으면 이다.
소스 링크
7. 팰린드롬 경로 3
일단 백트래킹이 생각나지만, 탐색해야 할 상태가 너무 많고, 시간 안에 나오지 못할 것이 자명한 것 같다.
이제 다르게 생각해보자. 경로가 팰린드롬이라는 것은,
- 시작점에 적혀있는 글자 = 도착점에 적혀있는 글자
- 두 번째 점에 적혀있는 글자 = 뒤에서 두 번째 점에 적혀있는 글자
- …
이런 꼴이어야 할 것이다.
입력으로 들어온 격자를 A
라고 하자.
DP를 생각할 수 있다.
dp[a][b][c][d] = 시작점으로부터 (a,b), 도착점으로부터 (c,d)까지, 팰린드롬이 되도록 도달하는 방법의 수
A[a][b] ≠ A[c][d]
이면 이 값은 자명하게 0일 것이다.
아닐 경우, 시작점은 (a,b-1)
혹은 (a-1,b)
에서 오고, 도착점은 (c,d+1)
혹은 (c+1,d)
에서 왔을 것이다.
각각의 값을 가져와주면 될 것이다.
길이를 고려해야 하는 것이 아닌가, 하는 생각이 들 수도 있겠다. 하지만 (1,1)
에서 (a,b)
까지 도달하려면 정확히 a+b-2
번 움직여야 한다. (n,n)
에서 (c,d)
까지도 마찬가지로 2n-c-d
번 움직여야 한다. 이 둘이 같기만 하면 상관이 없다. 만일 그 값이 가 되었다면 답에 더해주면 될 것이다.
소스 링크
8. 레슬러
수여하는 금화 중, 각 선수가 승리한 경기의 수는 항상 일정하고, 레슬러들의 순서를 바꿔도 영향을 받지 않는다. 순서를 바꾸는 것은 자기보다 앞에 있는데 자기가 이긴 선수의 수
에 영향을 주는데, 이 값을 최소화시켜야 할 것이다.
이 값을 0으로 만들 수 있을까…?
어떤 선수의 앞에는 자기가 지는 선수들만 있도록 배치할 수 있다면, 그 값이 0이 될 것이다. 이를 위해서는 세 선수 A, B, C가 있을 때, A가 B를 이기고 B가 C를 이긴다면, A는 C를 이긴다는 것이 필요하다. 이것이 가능하다면, 선수간에 이기고 지는 관계를 기준으로 정렬할 수 있다. 정렬된 상태로 배치하면 모든 선수의 앞에는 자기가 지는 선수들만 있도록 배치될 것이다.
이제 그것이 성립하는지 고민해보자. 와 를 각각 번째 레슬러의 ‘힘’과 ‘마술 링의 힘’이라고 하자.
A가 B를 이기는 조건은 다음과 같다.
정리하면
즉, 의 ‘경기력’은, 실제로는 상대에 영향을 받지 않고, 선수 고유의 값인 에만 의존하게 된다. 이 값에 따라 항상 승부가 결정이 나게 된다. 때문에 A가 B를 이기고, B가 C를 이기면, 이들의 값의 크기 순서에 의해, 자명하게 A가 C를 이기게 된다.
즉 선수들을 승부 결과에 따라 정렬하면 된다! 여기서 우리가 A에 의존하는 값과 B에 의존하는 값을 서로 분리하려고 노력(했고 결국 성공)했다는 점을 곱씹어보자.
참고로, 이 값을 double
로 들고 다니며 사용해도 된다! 값의 범위가 1000 이하이기도 하고, 만약 double
의 오차가 중요해질 만큼 값이 비슷하다면 값이 같은 경우일 확률이 매우 높은데, 이 문제에서는 비기는 경우가 없다고 했기 때문이다. 하지만 double
을 사용하는 것은 매우 매우 매우 매우 매우 위험하기 때문에, 꼭 쓰고싶은 경우가 아니면 되도록이면 쓰지 말자.
9. 커플 깨기
- indegree
- 어떤 노드에 대해, 그 노드로 들어오는 간선의 수
- outdegree
- 어떤 노드에 대해, 그 노드에서 나가는 간선의 수
- degree
- 어떤 노드에 대해, 그 노드와 연결된 간선의 수.
방향 그래프에서는 보통 indegree + outdegree를 나타낸다.
무방향 그래프에서 모든 degree의 합은 항상 짝수이다.
증명 | 간선 하나가 degree의 합을 2씩 증가시키기 때문.
원하는 조건을 만족시키면
- degree가 홀수인 노드의 경우, indegree = outdegree ± 1
- degree가 짝수인 노드의 경우, indegree = outdegree
일 것이다.
만약 모든 노드의 degree가 짝수라면 어떻게 풀까?
모든 노드의 degree가 짝수라면 그래프의 각각의 component에 대해 항상 오일러 회로(Eulerian circuit, Eulerian cycle)이 존재한다.
오일러 회로란 한 정점에서 시작해 모든 간선을 지나고 다시 시작점으로 돌아오는 경로를 뜻한다.
오일러 회로를 쭉 돌며, 회로를 지나는 방향으로 간선의 방향을 고정해준다고 해보자.
그러면 모든 정점에 대해, 그 정점에 한 번 들어오면 한 번 나가게 되므로, indegree와 outdegree가 같아진다. 즉 문제에서 요구하는 조건을 만족하게 된다.
이제 degree가 홀수인 정점이 있다고 하자.
가상의 정점 을 만들어, 모든 점을 여기에 연결해보자.
그러한 정점은 항상 짝수 개 있으므로, 의 degree 또한 짝수이다.
그리고 각각의 홀수인 정점 또한 degree가 1 증가하므로, 모든 정점의 degree가 짝수가 된다.
이제 위에서 말한 작업을 한 뒤, 과 그와 연결된 간선들을 제거했다고 하자.
각각의 홀수인 정점들은 indegree와 outdegree가 같은 상태였다가, 의 제거에 의해 indegree와 outdegree가 1 차이나게 된다.
각각의 짝수인 정점들은 변화가 없다.
따라서 문제에서 요구하는 사항을 만족할 수 있게 된다.
소스 링크
10. 램프
이웃한 점들을 이은 ‘직선’들을 생각했을 때, 모든 직선보다 위에 있는 점 중 y좌표가 가장 작은 것을 찾는 문제이다.
1. Convex Hull을 이용한 방법
직선들을 기울기 순으로 정렬한 후, stack을 이용하여 convex hull을 구할 수 있다.
그 후 convex hull을 이루는 이웃한 직선들간의 교점 중 y좌표가 가장 작은 것을 찾는다.
시간복잡도는 정렬의 + 처리
2. 이분탐색을 이용한 방법
먼저, 어떤 높이 y에서 모든 지면을 비출 수 있다고 하자.
그러면 그 점에서 y좌표를 더 높이기만 하면 항상 모든 지면을 비출 수 있을 것이다.
그렇다면 이분탐색을 적용할 수 있게 된다.
‘높이 x에 램프를 놓아 모든 지면을 비출 수 있는가?’라는 질문에 대해 대답할 수 있다고 해보자.
최소 높이가 될 수 있는 범위 을 놓고, 라고 했을 때,
에 대해 그 질문에 대한 답이 yes이면 으로,
답이 no이면 으로 만들어줄 수 있다.
그러면 최소 높이가 될 수 있는 범위가 절반이 된다.
이 과정을 넉넉하게 50번정도 수행한다면, 이 범위는 배가 되므로, 답을 거의 완벽하게 맞출 수 있다.
이제 ‘높이 x에 램프를 놓아 모든 지면을 비출 수 있는가?’라는 질문에 대해 생각해보자.
높이 x에 램프를 놓았다면, 어떤 직선에 대해 램프가 그 직선보다 위쪽에 있어야 하므로, 램프가 있을 수 있는 x좌표의 범위가 혹은 꼴로 한정된다.
모든 직선에 대해 이러한 범위들의 교집합을 구했을 때, 그 교집합이 공집합이 아니라면, 그 집합 안의 어떤 점에 램프를 놓을 수 있다는 뜻이 된다. 즉 해당 교집합이 공집합이 아닌가의 여부를 대답하면 된다.
Written with StackEdit.
'알고리즘 문제풀기 > 각종 OJ 풀이' 카테고리의 다른 글
AtCoder (0) | 2016.09.04 |
---|---|
나코더 방학 연습 2주차 풀이 (0) | 2016.08.07 |
1591: 수열 복원 (0) | 2016.03.08 |
[algospot] [NUMB3RS] 두니발 박사의 탈옥 (0) | 2014.05.30 |
[KOISTUDY] [017D] [381] 격자 읽기 (0) | 2014.02.25 |