[Programmers/파이썬]프로그래머스(Lv.2) 두 큐 합 같게 만들 문제풀이
문제 설명
길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(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 고려가 필요합니다.
입출력 예queue1queue2result
[3, 2, 7, 2] | [4, 6, 5, 1] | 2 |
[1, 2, 1, 2] | [1, 10, 1, 2] | 7 |
[1, 1] | [1, 5] | -1 |
입출력 예 설명
입출력 예 #1
문제 예시와 같습니다.
입출력 예 #2
두 큐에 담긴 모든 원소의 합은 20입니다. 따라서, 각 큐의 합을 10으로 만들어야 합니다. queue2에서 1, 10을 순서대로 추출하여 queue1에 추가하고, queue1에서 1, 2, 1, 2와 1(queue2으로부터 받은 원소)을 순서대로 추출하여 queue2에 추가합니다. 그 결과 queue1은 [10], queue2는 [1, 2, 1, 2, 1, 2, 1]가 되며, 각 큐의 원소 합은 10으로 같습니다. 이때 작업 횟수는 7회이며, 이보다 적은 횟수로 목표를 달성하는 방법은 없습니다. 따라서 7를 return 합니다.
입출력 예 #3
어떤 방법을 쓰더라도 각 큐의 원소 합을 같게 만들 수 없습니다. 따라서 -1을 return 합니다.
사고 흐름
큐를 다루는 문제구나
-> 두 큐의 합을 각각 구하고 총합이 큰 큐에서 작은 큐로 원소를 하나씩 보내자
-> 그때 돈 횟수가 큐들의 길이 합보다 길면(= 모든 원소를 다 이동해봤는데도 해결못함) -1을 리턴한다
-> 하지만 이 방법으로 접근하면 3문제 정도 시간 초과가 났다. 그래서 별별 방법을 시도해보고, 시간 복잡도를 줄이기 위해 노력했지만 안됐다.. 결국 완전 다시 풀었다
-> 두번째 방법: 아마 두개의 큐를 다룰땐 pop이 많아서 pop을 최대한 덜 쓰는 방법으로 생각을 해봤다. pop을 안쓰거나 한번만 쓸 수 있을까?
-> 하나의 리스트(큐)에서 i,j로 각 큐의 시작을 나타냈다.
-> 인덱스를 +1하면서 큐의 pop과 insert의 기능을 대신했다
문제 핵심과 알고리즘
문제 자체는 쉬웠지만 시간초과가 안나는 자료구조와 알고리즘을 찾는게 중요한 문제
(두 개의 큐를 다뤘지만 시간초과가 났고, 두 개의 큐를 대체할 수 있는 리스트를 활용하는 문제)
(첫번째 방법: 3문제 시간초과남)
def solution(queue1, queue2):
answer = -1
sum1=sum(queue1)
sum2=sum(queue2)
while True :
answer+=1
if answer>=len(queue1)+len(queue2)+ 10 :
print("1: ", answer)
return -1
if sum1==sum2 :
return answer
elif sum1>sum2 :
a=queue1[0]
sum1-=a
sum2+=a
queue2.append(queue1.pop(0))
else :
a=queue2[0]
sum1+=a
sum2-=a
queue1.append(queue2.pop(0))
print(answer)
return answer
(두 번째 방법)
def solution(queue1, queue2):
answer = -1
sum1=sum(queue1)
sum2=sum(queue2)
queue=queue1+queue2
i, j = 0, len(queue1)
while True :
answer+=1
if answer>=len(queue)+10 :
return -1
if sum1==sum2 :
break
elif sum1>sum2 :
sum1-=queue[i]
sum2+=queue[i]
i+=1
i=i%len(queue)
else :
sum1+=queue[j]
sum2-=queue[j]
j+=1
j=j%len(queue)
return answer
문제 풀면서 헤맸던 부분
1. return -1
if answer>=len(queue)에서 원래 +10이 없었는데 자꾸 한 문제가 틀렸다. 뭔가 이 부분이 애매해서 +10을 했더니 됐다. 뭔가 찝찝하긴 한데.. 그래도 통과했으니 뭐..
다른 풀이 참고하여 배운 점
def solution(queue1, queue2):
indicator2=sum(queue1)-int(sum(queue1+queue2)/2)
answer=0
sub_list=(queue1+queue2+queue1)[::-1]
add_list=(queue2+queue1+queue2)[::-1]
while indicator2!=0:
try:
if indicator2>0:
indicator2-=sub_list.pop()
else:
indicator2+=add_list.pop()
except:
return -1
answer+=1
return answer
indicator로 각각의 큐의 합의 차이를 구했고, sub_list와 add_list에는 큐의 원소를 가져올 수 있는 큐의 원소들을 넣었다.
그리고 차이가 양수이면(큐1이 더 크면) 빼주고, 차이가 음수이면 더해줬다. try구문을 써서 pop을 할 수 없을때까지 다 시도했는데 해결하지 못하면 -1을 리턴했다. 나는 pop(i)를 안쓰려고 했는데 여기서는 pop()을 사용해서 시간 초과가 안난것 같다.
개념 정리
1. 시간 복잡도
https://wayhome25.github.io/python/2017/06/14/time-complexity/
파이썬 자료형 별 주요 연산자의 시간 복잡도 (Big-O) · 초보몽키의 개발공부로그
파이썬 자료형 별 주요 연산자의 시간 복잡도 (Big-O) 14 Jun 2017 | 들어가기 알고리즘 문제를 풀다 보면 시간복잡도를 생각해야 하는 경우가 종종 생긴다. 특히 codility는 문제마다 시간복잡도 기준
wayhome25.github.io