정수들이 섞여있는 수열에서 연속하는 정수 수열을 O(N)에 분리하기
11232번: Shuffles (acmicpc.net)
주어진 수열이 최소 몇번의 shuffle로 이루어진 수열인지 구하는 문제
셔플방법은 수열에서 어떤 특정 지점에서 두 부분으로 나누고 두 부분을 순서를 유지한채 섞는 것이다.
1 2 3 4 5라면..
1 2 3 4 5로 나누고
1 4 2 5 3 처럼 섞는 것
주어진 수열에서 어떤 특정 지점 k에서 나뉘었다고 한다면, 2개의 부분 수열
1,2,3,...,k
k+1,k+2,...,n으로 나뉘고 이것이 섞일 것이다.
이렇게 섞인 수열에서 또 한번 나누면 최대 4개의 부분 수열
1,2,3,..,k1
k1,k1+1,...k
k+1,k+2,..k2
k2+1,k2+2,...,n으로 구해진다.
여기서 또 한번 나누면 최대 8개의 부분 수열을 얻을 수 있다.
....
이런 식으로 y번 shuffle했다면 최대 2^y개의 부분 수열을 얻을 수 있다.
예를 들어 1,2,3,4,5,6에 대해 생각해보면
1,2,3
4,5,6으로 나누고
1,4,2,5,3,6으로 섞은 다음
1,4,2
5,3,6에서
1,5,4,3,2,6으로 섞으면
1 2345 6으로 4개의 부분 수열을 얻는다
어떻게 섞느냐에 따라 이보다 더 적은 부분 수열을 가질 수 있는데
1,2,34,5,6으로 나눈 다음
1,2,4,3,5,6으로 섞고 다시
1,2,43,5,6으로 나눈 다음
1,3,2,4,5,6으로 섞으면
1 23 4 5 6으로 2개의 부분 수열만 나온다
----------------------------------------------------------------------------------------------------------------------------------
어쨌든 먼저 주어진 수열에서 연속하는 부분 수열이 몇개인지 찾아야한다
그 개수가 x개이고 y번 섞었다고 한다면, $x <= 2^{y}$를 만족시키는 y를 구하면 된다.
예를 들어
1 2 7 3 8 9 4 5 10 6은
1,2,3,4,5,6
7,8,9,10으로 2개 있다.
그래서 1번 섞은 것이다.
3 8 1 9 4 5 2 7 10 6은
1 23 4 5 678 9 10으로 4개 있다.
그래서 2번 섞었다.
2 1 4 3 6 5 8 7은
12 34 56 78로 5개 있다.
그래서 3번 섞었다고 할 수 있다.
이걸 어떻게 찾을 수 있을까? $O(N^{2})$말고 생각나는게 없는데...
--------------------------------------------------------------------------------------------------------------------------------------------------
배열을 순회하면서 현재 A[i]가 아직 방문하지 않았다면, 이것이 새로운 부분 수열의 시작점이고, A[i],A[i]+1을 방문표시 해준다.
A[i]가 방문되어 있다면 이미 이전에 부분수열이 있고 A[i]+1을 방문표시 해준다.
핵심은 A[i]+1에 방문표시를 해두는 것
예를 들어 2,1,4,3,6,5,8,7의 경우
먼저 2는 아직 방문하지 않았으므로, 부분 수열의 개수 + 1해주고, 2,3을 방문표시 해준다.
2 다음에 3이 나올 것이라고 예상되기 때문이다.
그래서 3이 나온다면 이전에 있는 부분 수열의 뒤에 나올 것이기 때문에 미리 표시를 해둔다.
다음 1은 아직 방문하지 않았으므로 부분 수열의 개수 + 1해주고, 1,2를 방문표시 해준다.
현재
2,31 로 2개 찾은 것
다시 4가 나온다면 아직 방문하지 않았으므로 부분 수열의 개수 + 1 해주고, 4,5 방문표시 해준다
2,314,5로 3개 찾은 것
다음 3은 방문표시 되어있으니 4에 방문표시를 해주고 넘어가고 6이 나온다면?
아직 방문하지 않았으므로 부분 수열의 개수 +1 해주고 6,7 방문표시 해준다
2,314,56,7로 4개 찾았다.
5는 방문표시 되었으니 6에 방문표시 해주고 넘어가고
8은 아직 방문하지 않았으니 부분 수열의 개수 +1 해주고 8,9 방문표시 해준다
2,314,56,78,9로 5개 찾았다.
9는 실제로 없지만 알고리즘을 위해 넣어준 것이고, 다음 7은 이미 방문표시 되어있으니 8에 방문표시 해주고 넘어간다.
n = int(input())
A = list(map(int,input().split()))
visited = [0]*(n+2)
#연속하는 부분 수열의 개수
count = 0
for i in range(n):
if visited[A[i]] == 0:
count += 1
visited[A[i]] = 1
visited[A[i]+1] = 1
#2^y >= count인 y가 셔플 횟수
v = 1
y = 0
while v < count:
v *= 2
y += 1
print(y)
'알고리즘 > 애드 혹 알고리즘' 카테고리의 다른 글
n으로 나누어 떨어지면서 자릿수의 합도 n으로 나누어 떨어지는 정수 (0) | 2024.06.01 |
---|---|
배열을 뒤집었을때 생기는 반전 수의 개수 구하기 (0) | 2024.05.23 |
무방향 연결그래프에서 정점의 성질 관찰하기 (0) | 2024.05.10 |
ABC 351 F번 복기 - 알고리즘 문제에 max함수를 바꾸는 트릭 2가지 (0) | 2024.05.02 |
코딩테스트 복기 - 복잡한 그래프 생각하면서 효율적으로 탐색 하기 (0) | 2024.02.03 |