두 큐 합 같게 만들기

dasd412·2022년 9월 10일
0

코딩 테스트

목록 보기
63/71

문제 설명

길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(pop)하고, 추출된 원소를 다른 큐에 집어넣는(insert) 작업을 통해 각 큐의 원소 합이 같도록 만들려고 합니다. 이때 필요한 작업의 최소 횟수를 구하고자 합니다. 한 번의 pop과 한 번의 insert를 합쳐서 작업을 1회 수행한 것으로 간주합니다.

큐는 먼저 집어넣은 원소가 먼저 나오는 구조입니다. 이 문제에서는 큐를 배열로 표현하며, 원소가 배열 앞쪽에 있을수록 먼저 집어넣은 원소임을 의미합니다. 즉, pop을 하면 배열의 첫 번째 원소가 추출되며, insert를 하면 배열의 끝에 원소가 추가됩니다. 예를 들어 큐 [1, 2, 3, 4]가 주어졌을 때, pop을 하면 맨 앞에 있는 원소 1이 추출되어 [2, 3, 4]가 되며, 이어서 5를 insert하면 [2, 3, 4, 5]가 됩니다.

다음은 두 큐를 나타내는 예시입니다.

queue1 = [3, 2, 7, 2]
queue2 = [4, 6, 5, 1]
두 큐에 담긴 모든 원소의 합은 30입니다. 따라서, 각 큐의 합을 15로 만들어야 합니다. 예를 들어, 다음과 같이 2가지 방법이 있습니다.

queue2의 4, 6, 5를 순서대로 추출하여 queue1에 추가한 뒤, queue1의 3, 2, 7, 2를 순서대로 추출하여 queue2에 추가합니다. 그 결과 queue1은 [4, 6, 5], queue2는 [1, 3, 2, 7, 2]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 7번 수행합니다.
queue1에서 3을 추출하여 queue2에 추가합니다. 그리고 queue2에서 4를 추출하여 queue1에 추가합니다. 그 결과 queue1은 [2, 7, 2, 4], queue2는 [6, 5, 1, 3]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 2번만 수행하며, 이보다 적은 횟수로 목표를 달성할 수 없습니다.
따라서 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수는 2입니다.

길이가 같은 두 개의 큐를 나타내는 정수 배열 queue1, queue2가 매개변수로 주어집니다. 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요. 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요

입출력 조건

1 ≤ queue1의 길이 = queue2의 길이 ≤ 300,000
1 ≤ queue1의 원소, queue2의 원소 ≤ 109
주의: 언어에 따라 합 계산 과정 중 산술 오버플로우 발생 가능성이 있으므로 long type 고려가 필요합니다.

전체 코드 (93.3)

from collections import deque

def solution(queue1, queue2):
    answer = 0
    
    sum1,sum2=0,0
    max1,max2=0,0

    for i in range(len(queue1)):
        sum1+=queue1[i]
        max1=max(max1,queue1[i])
        
        sum2+=queue2[i]
        max2=max(max2,queue2[i])
    
    max_value=max(max1,max2)
    
    # 두 큐의 합을 모두 합친 값 //2 보다 큰 값이 한 큐에 담겨 있다면, 각 큐의 원소 합을 같게 할 수 없다.
    if (sum1+sum2)//2<max_value:
        return -1
    
    dq1=deque(queue1)
    dq2=deque(queue2)
    
    # 더 값이 작은 쪽에게 나눠주는 그리디 풀이
    while True:
        
        if sum1<sum2:
            pop=dq2.popleft()
            dq1.append(pop)
            sum1+=pop
            sum2-=pop
        elif sum1>sum2:
            pop=dq1.popleft()
            dq2.append(pop)
            sum1-=pop
            sum2+=pop
        else:
            break
            
        answer+=1    
        
    return answer

테스트 케이스 11, 30이 시간 초과 걸린다.

반례

[1,1] ,[1,2] 인 경우 위 코드에서 -1로 걸리지 않는다. 그리고 while 1에 의해 무한 루프가 걸려 시간 초과난다.


정답 코드 (100.0점)

from collections import deque

def solution(queue1, queue2):
    answer = 0
    
    sum1,sum2=sum(queue1),sum(queue2)

    dq1=deque(queue1)
    dq2=deque(queue2)
    
    i=0
    # 더 값이 작은 쪽에게 나눠주는 그리디 풀이
    while i<len(queue1)*4:
        
        if sum1<sum2:
            pop=dq2.popleft()
            dq1.append(pop)
            sum1+=pop
            sum2-=pop
        elif sum1>sum2:
            pop=dq1.popleft()
            dq2.append(pop)
            sum1-=pop
            sum2+=pop
        else:
            return answer
            
        answer+=1    
        i+=1
        
    return -1

나름의 해설

문제에서 작업의 최소 횟수를 해로 원하기 때문에 그리디 또는 dp로 풀 수 있을 거라 생각하였다. 그리고 배열의 길이가 최대 30만이 되기 때문에 재귀는 절대 활용할 수 없을 거라 생각했다.

일단, 풀이는 두 배열의 합을 비교해서 더 큰쪽이 더 작은 쪽에게 나눠주는 그리디 풀이다.
그런데 그리디는 엄밀한 증명이 제일 어렵다. 이 방법을 사용하면 해결책이 될 수 밖에 없다는 증명이 필요한데, 직관적으로 푼 것이라 아쉽다...

그리고 반복문의 조건은 len(queue1)*4로 정하였다. 그림으로 그려보면 다음과 같다.

처음에 q1과 q2를 전부 교환하면 len(queue1)*2의 연산이 필요하다.

다시 q1과 q2를 전부 교환하면 len(queue1)*4의 연산이 필요하다. 완전 제 자리로 돌아온 것이기 때문에, 이 연산 횟수가 지나도 끝내지 못하면 절대로 각 큐의 합을 같게 할 수 없다고 볼 수 있다.


profile
아키텍쳐 설계와 테스트 코드에 관심이 많음.

0개의 댓글