0713 How many shifts when doing an insertion sort

ph
이동: 둘러보기, 검색

해커랭크의 Insertion Sort Advanced Analysis

솔직히 드럽게 오래 걸림. 결국 Discussions보고 binary indexed tree(BIT)로 해결했다.
\(O( n \log n)\)과 \(O(n^2)\)는 정말 무시무시한 차이였음. n이 100k에 이르면 \(O(n^2)\)는 끝나지도 않는다.

아래는 내 것인데, 구간합을 구하는 빠른 방법이 BIT를 이용하는 것이어서 그대로 한 것이지만, 뒤에 <Problem setter's code>를 보면, 이 답은 정말 바보같다는 것을 알 수 있다.

# Enter your code here. Read input from STDIN. Print output to STDOUT
#!/usr/bin/python
def getsum(idx, itvsum):
    sumv = 0
    while idx>0:
        sumv += itvsum[idx]
        idx -= (idx & -idx)
    return sumv

n = input()
for iterate in range( n ):
    x = input()
    a = [ int( i ) for i in raw_input().strip().split() ]
    b = sorted(set(a)) # what the...
    num2idx = {}
    for i, val in enumerate(b):
        num2idx[val] = i+1
    ln2i = len(num2idx)

    interval_sum = [0]*(ln2i+1) # 1 base
    ans = 0
    total_cnt =0

    for ai in a:
        idx = num2idx[ai]
        #get sum
        ans += total_cnt - getsum(idx, interval_sum)

        #insert new element to interval_sum
        while idx <= ln2i:
            interval_sum[idx] += 1
            idx += (idx & -idx)
        total_cnt += 1
    
    print ans


Problem setter’s code

아래는 짧고 아름다운 모범답안.훌륭하다 훌륭해 정말 훌륭하다 두가지 성질을 이용한다.
하나는, shift의 개수를 셀 때, element를 하나씩 넣는 것이 아니고 아예 sorted array 둘을 합하는 과정으로 이해해도 구할 수 있다는 점을 이용. 즉, sorted array A, B가 있을 때, cost([A, B]) = cost(A) + cost(B) + \(\alpha\)이고 sorted array라는 가정이 있기 때문에 \(\alpha\)를 쉽게 계산할 수 있다.

def inversions(arr):
    n = len(arr)
    if n==1:
        return 0
    n1 = n/2
    n2 = n - n1
    arr1 = arr[:n1]
    arr2 = arr[n1:]
    ans = inversions(arr1) + inversions(arr2)
    i1 = 0
    i2 = 0
    for i in range(n):
        if i1 <n1 and (i2>=n2 or arr1[i1]<=arr2[i2]):
            arr[i] = arr1[i1]
            ans += i2
            i1 += 1 
        elif i2 < n2:
            arr[i] = arr2[i2]
            i2 += 1
    return ans

for _ in range(input()):
    n = input()
    arr = map(int,raw_input().split())
    counts = inversions(arr)
    print counts

다른 하나는, 15th line이 핵심. 각각의 element의 inversion을 계산하고 넘어가는 것이 아니라, 통째로 몇개의 inversion이 하나씩 증가하고 있다고 생각하고 연산. \(O(n)\)에 \(\alpha\)계산이 가능해진다. 웹에 같은 문제를 푼 사람이 있는데 필요하면 그 사람의 설명도 참조.

etc

두 코드의 속도를 비교해보면 내 코드가 상당히 빠르게 동작한다. 답안의 재귀호출이 시간을 상당히 많이 잡아먹는 모양. 대략 답안코드 수행시간의 70% 미만으로 걸린다. 아무리 빨라도 구린 것은 구린 것이다. 아니면 100배 빠르던가 sorted(set(a))라는 어처구니 없는 행동을 해도 수행시간이 더 빠른것 보면 native method의 무시무시함을 알 수 있다.



blog comments powered by Disqus