길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(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가지 방법이 있습니다.
따라서 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수는 2입니다.
길이가 같은 두 개의 큐를 나타내는 정수 배열 queue1
, queue2
가 매개변수로 주어집니다. 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요. 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요.
queue1
의 길이 = queue2
의 길이 ≤ 300,000queue1
의 원소, queue2
의 원소 ≤ 10 9queue1 | queue2 | result |
---|---|---|
[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 , 3 , 4 , 5]
위와 같은 배열이 있다고 한다.
<초기 단계>
시작점과 끝점이 첫번째 원소의 인덱스를 가리키도록 합니다.
⬇️(start)
[1 , 2 , 3 , 4 , 5]
⬆️(end)
→ 현재의 부분 합은 1 카운트는 0
<Step 1>
이전 단계에서의 부분합이 1 → end를 증가시킵니다.
⬇️(start)
[1 , 2 , 3 , 4 , 5]
⬆️(end)
→ 현재의 부분 합은 3 카운트는 0
<Step 2>
부분합이 3 → end 를 증가시킵니다.
⬇️(start)
[1 , 2 , 3 , 4 , 5]
⬆️(end)
→ 현재의 부분 합은 6 카운트는 0
<Step 3>
부분합 6 → start를 증가시킵니다.
⬇️(start)
[1 , 2 , 3 , 4 , 5]
⬆️(end)
→ 현재의 부분 합은 5 카운트는 1 (부분합이 5이기 때문에)
이걸 계속 반복합니다.
<마지막>
시작점과 끝점이 첫번째 원소의 인덱스를 가리키도록 합니다.
⬇️(start)
[1 , 2 , 3 , 4 , 5]
⬆️(end)
→ 현재의 부분 합은 5
그래서 이 개념을 우리 문제에 적용해본다면
주황색은 q1 포인터 초록색은 q2 포인터 입니다. (출처)
[초기단계] : 같은 합일 때가 10 이고 sum1이 같은 합 즉 half 보다 작습니다.
1 | 2 | 1 | 2 | 1 | 10 | 1 | 2 |
---|
sum1 : 6 sum2: 14
half: 10
sum1 < half
[2단계] : queue2의 시작점을 옮깁니다. 여전히 sum1은 half 보다 작습니다.
1 | 2 | 1 | 2 | 1 | 10 | 1 | 2 |
---|
sum1 : 7 sum2: 13
half: 10
sum1 < half
[3단계] : queue2의 시작점을 또 옮깁니다. sum1이 half 보다 큽니다.
1 | 2 | 1 | 2 | 1 | 10 | 1 | 2 |
---|
sum1 : 17 sum2: 3
half: 10
sum1 > half
[4단계] : queue1의 시작점을 옮깁니다. 여전히 sum1이 half 보다 큽니다.
1 | 2 | 1 | 2 | 1 | 10 | 1 | 2 |
---|
sum1 : 16 sum2: 3
half: 10
sum1 > half
.
.
.
이런식으로 계속 반복하다 보면 아래와 같은 결과가 나옵니다.
[마지막단계] : queue1의 시작점을 옮깁니다. 여전히 sum1이 half 보다 큽니다.
1 | 2 | 1 | 2 | 1 | 10 | 1 | 2 |
---|
sum1 :10 sum2: 10
half: 10
sum1 == half && sum2 == half
이렇게 결과 값을 찾을 수 있습니다.
<원래 제가 생각한 풀이 방식>
일단 큐1,큐2에 있는 모든 수를 더해서 나누기 2 해서 같은 합을 찾습니다.
큐1의 합, 큐2의 합을 각각 구합니다.
큐1 혹은 큐2 중에 같은 합 보다 큰 경우에 해당 큐에 있는 값을 빼서 다른 큐에 넣어주는 것을 반복해서 큐1의 합과 큐2의 합이 모두 같은 합에 도달하는 경우 반복문을 빠져나오도록 설계했습니다.
반복문을 돌면서 원소의 값 중 하나라도 같은 합 값보다 큰 것이 있다면 -1을 반환하도록 했습니다.
function solution(queue1, queue2) {
//하나 빼서 하나 넣으면 두 큐의 값이 같도록 하기
//한번의 팝과 한번의 인서트를 묶어서 작업을 1회 한것으로 간주
//팝하면 배열의 첫번째 원소가 추출
//인서트하면 배열의 끝에 원소 추가
//모든 값의 합을 구하고
//큐 1번,2번의 합,같은합
let addQ1=0,addQ2=0,midQ=0;
for(let i=0;i<queue1.length;i++){
addQ1+=queue1[i];
addQ2+=queue2[i];
}
midQ= (addQ1+addQ2)/2;
//같은 합이 정수가 아니라면 -1 반환
if(!Number.isInteger(midQ)) return -1;
var answer = 0;
while(true){
if(addQ1 > midQ){
let a = queue1.shift();
if(a>midQ){ return -1;}
queue2.push(a);
addQ1 -= a;
addQ2 += a;
}
else if(addQ2 > midQ){
let a = queue2.shift();
if(a>midQ){ return -1;}
queue1.push(a);
addQ1 += a;
addQ2 -= a;
}
else{ return -1;}
answer++;
if(addQ1 === midQ && addQ2 === midQ) break;
}
return answer;
}
function solution(queue1, queue2) {
let sum1 = queue1.reduce((prev,cur)=>prev+cur,0);
let sum2 = queue2.reduce ((prev,cur)=>prev+cur,0);
const q = [...queue1,...queue2];
const half = (sum1 + sum2 )/2;
let q1Pointer = 0;
let q2Pointer = queue1.length;
for(let cnt =0 ; cnt<queue1.length * 3 ; cnt++){
if(sum1 === half){
return cnt;
}
sum1 =
sum1 > half
? sum1 - q[q1Pointer++ % q.length]
: sum1 + q[q2Pointer++ % q.length];
}
return -1;
}
.
.
.
.
.
.
공부는 언제나 힘드네영...