띠리링구 2022. 3. 24. 17:26

https://programmers.co.kr/learn/courses/30/lessons/72414?language=python3 

 

코딩테스트 연습 - 광고 삽입

시간을 나타내는 HH, H1, H2의 범위는 00~99, 분을 나타내는 MM, M1, M2의 범위는 00~59, 초를 나타내는 SS, S1, S2의 범위는 00~59까지 사용됩니다. 잘못된 시각은 입력으로 주어지지 않습니다. (예: 04:60:24, 11

programmers.co.kr

 

일단 내가 문제를 처음 보고 떠올린 아이디어는

1. 초로 환산해야 한다.

2. 매 초마다의 동시 재생 수를 구해야 한다.

3. sliding window로 누적 재생 시간을 계산하며 최적의 시작 지점을 찾는다.

였다.

 

내가 지금 피시방이라 코딩하기가 좀 힘든 환경이어서 아이디어를 떠올린 뒤에 그냥 바로 해설을 읽어봤다. (어차피 정답률 1%문제라서 내가 혼자 풀 수 있을거란 자신감도 없었다..ㅋㅋㅋ)

 

해설도 매 초마다 뭔가를 작업한다는 아이디어는 나랑 공통적이었다. 근데 저걸 좀 더 영리하게 했다. 약간 적분하는 느낌?? (나중에 다시 생각해봤는데 이렇게 하지 않으면 절대 문제를 효율적으로 풀지 못한다.. 대체 이런발상은 어떻게한거야;;; 오늘도 자괴감up!!)

1. total_time[x] = x 시각에 시작된 재생 구간의 개수 – x 시각에 종료된 재생구간의 개수

logs를 순회하며 기록하면 된다.

뭐 [1,1,1,1,-1,-1,-1,-1]

[2,0,-1,3,-1,-2,-1,0]

이런식으로 나오겠지

2. total_time[i] = total_time[i] + total_time[i - 1]

이렇게 전 요소를 후 요소에다 더해주면 그 초에 동시 재생 수가 된다.

[1,1,1,1,-1,-1,-1,-1]를 예로들면

[1,2,3,4,3,2,1,0]이 되겠지.

그니까 [3초~4초) 에는 4명이 동시 시청중이었다는 거겠지? 

 

3. total_time[i] = total_time[i] + total_time[i - 1]

이걸 한 번 더 해주면 0초부터 매초까지의 누적 시청 초를 구할 수 있다.

[1,2,3,4,3,2,1,0]를 예로 들면

[1,3,6,10,13,15,16,16]

그니까 0초~5초까지 모든 시청자들의 누적 시청시간을 합치면 13초라는거다.

그럼 x초~y초까지의 누적 시청 시간은 차를 이용해서 구할 수 있다. total_time[y]-total_time[x]이런식으로 하면 되겠지?

 

4. 근데 마지막 과정이 이해가 안된다. ㅋㅋㅋ 해설을 그대로 복붙해보겠다.

마지막으로 정답을 구하는 과정입니다.

for i = (adv_time_sec - 1) ~ (play_time_sec - 1)
    if ( i >= at) 
        max_time = max(max_time, total_time[i] - total_time[i - at] )
    else 
        max_time = max(max_time, total_time[i])

위 코드는 반복문을 돌며 시각 i - at + 1에 광고를 넣을 때의 누적 재생 시간을 구하여, 그중에서 가장 긴 시간을 max_time에 넣어주고 있습니다. max_time 값이 마지막으로 업데이트될 때의 시각 i - at + 1을 HH:MM:SS 형태로 변환한 값이 문제에서 요구하는 정답입니다.

 

i가 광고길이만큼의 시점부터 끝까지 iteration이 도는건 이해하겠는데

그 내부에 at이 뭔질 모르겠네. 아..? adv_time 그러니까 광고의 길이인가? 그럼

total_time[i - at]가 이해가 된다.

if i>=at 그러니까 i를 광고의 마지막 지점으로 삽입할 수 있을만큼의 여유공간이 앞에 있으면

i-at+1에 광고를 넣는다고 가정하고 누적시간을 구해서 갱신하는거지.

i < at라면 0초부터 i초까지의 total 누적시간과 max_time을 그냥 비교해서 갱신하고.

근데 i<at인 경우가.. 제일 첫 iteration밖에 없지 않나? adv_time_sec - 1부터 시작하니까..

굳이 이걸 반복문 내에 제어문으로 넣었어야 했나 싶네.

 

def to_sec(timestr):
    h,m,s = tuple(map(int,timestr.split(':')))
    return h*3600+m*60+s

def to_str(timesec):
    h = timesec // 3600
    timesec %= 3600
    m = timesec // 60
    timesec %= 60
    s = timesec
    return f'{h:02d}:{m:02d}:{s:02d}'

def solution(play_time, adv_time, logs):
    playtime = to_sec(play_time)
    advtime = to_sec(adv_time)
    
    if advtime >= playtime:
        return "00:00:00"
    
    log_start = []
    log_end = []
    for log in logs:
        log = log.split('-')
        log_start.append(to_sec(log[0]))
        log_end.append(to_sec(log[1]))
    
    totaltime = [0] * (playtime+1)

    for i in range(len(logs)):
        totaltime[log_start[i]] += 1
        totaltime[log_end[i]] -= 1
    
    for i in range(1,playtime+1):
        totaltime[i] += totaltime[i-1]
        
    for i in range(1,playtime+1):
        totaltime[i] += totaltime[i-1]
        
    maxtime = totaltime[advtime-1]
    answer = 0
    for i in range(advtime,playtime):
        if totaltime[i]-totaltime[i-advtime] > maxtime:
            maxtime = totaltime[i]-totaltime[i-advtime]
            answer = i-advtime+1

    
    return to_str(answer)