어느 공원 놀이터에는 시소가 하나 설치되어 있습니다. 이 시소는 중심으로부터 2(m), 3(m), 4(m) 거리의 지점에 좌석이 하나씩 있습니다.
이 시소를 두 명이 마주 보고 탄다고 할 때, 시소가 평형인 상태에서 각각에 의해 시소에 걸리는 토크의 크기가 서로 상쇄되어 완전한 균형을 이룰 수 있다면 그 두 사람을 시소 짝꿍이라고 합니다. 즉, 탑승한 사람의 무게와 시소 축과 좌석 간의 거리의 곱이 양쪽 다 같다면 시소 짝꿍이라고 할 수 있습니다.
사람들의 몸무게 목록 weights
이 주어질 때, 시소 짝꿍이 몇 쌍 존재하는지 구하여 return 하도록 solution 함수를 완성해주세요.
- 2 ≤
weights
의 길이 ≤ 100,000 - 100 ≤
weights[i]
≤ 1,000- 몸무게 단위는 N(뉴턴)으로 주어집니다.
- 몸무게는 모두 정수입니다.
weights | result |
---|---|
[100,180,360,100,270] | 4 |
{100, 100} 은 서로 같은 거리에 마주보고 앉으면 균형을 이룹니다. {180, 360} 은 각각 4(m), 2(m) 거리에 마주보고 앉으면 균형을 이룹니다. {180, 270} 은 각각 3(m), 2(m) 거리에 마주보고 앉으면 균형을 이룹니다. {270, 360} 은 각각 4(m), 3(m) 거리에 마주보고 앉으면 균형을 이룹니다.
- Map을 이용하여 공통 숫자를 제거하고, 개수를 카운팅한다.
- Map을 통해 Set을 만들고, 오름차순으로 정렬한다.
- Set에 있는 숫자를 하나씩 가져와서 1:1, 1:2, 2:3, 3:4 비율 중 총족하는 비가 있는지 확인한다.
- 충족시, count에 가능한 경우의 수를 더해준다.
- 미충족시, 다음 조합을 확인한다.
function solution(weights) {
let answer = 0;
const wMap = new Map();
weights.forEach((w) => wMap.set(w, (wMap.get(w) || 0) + 1));
const wSet = [...wMap.keys()];
wSet.sort();
const ratios = [1, 4 / 3, 3 / 2, 2];
wSet.forEach((weight) => {
for (const ratio of ratios) {
const target = weight * ratio;
if (!Number.isInteger(target)) continue;
const count1 = wMap.get(weight);
const count2 = wMap.get(target);
if (weight === target) {
answer += (count1 * (count1 - 1)) / 2;
} else {
if (count2) answer += count1 * count2;
}
}
});
return answer;
}
1) 🤔 문제를 읽고 제한사항의 weights
를 보며 단순하게 조합이나 이중 for문으로 풀이하면 시간복잡도가 초과할 것이라 예상했다. 제한 사항을 고려한다면 최소한 O(NlogN)의 시간복잡도를 가지도록 고려해야 했다.
weights
의 길이가 10만이라도, 요소 weights[i]
의 값은 100~1000숫자 안에 들어갔다. 고로 중복을 제거하면 비교할 경우의 수가 현저하게 줄어듬을 알 수 있다.
그래서 먼저 Map을 이용하여 어떤 숫자가 있고, 각 숫자가 몇개씩 존재하는지 정리하면 된다고 생각했다.
2) 🤔 다음으로 고려해야할 점은 시소 짝궁이 되기 위해서 평행을 이루어야 하며, 이는 거리 x 무게
의 값이 같아야 충족했다. 이때 거리는 2, 3, 4m만 가능하므로 나올 수 있는 경우의 수는 [2:2, 2:3, 2:4, 3:2, 3:3:, 3:4, 4:2, 4:3, 4:4]이지만, 조합을 고려하고 중복을 제거한다면 [1:1, 1:2, 2:3, 3:4]로 4가지의 비율만 남게 된다.
그래서 1번에서 중복을 제거한 숫자를 기준으로 1배, 4/3배, 3/2배, 2배의 값을 구하고 그 값을 원소로써 가지고 있는지 확인하면 간단하게 개수를 추론할 수 있었다.
※ 풀이 과정은 맞았지만, 중간에 로직을 잘못 작성해서 제법 풀이 시간이 길어졌다. 이런 실수를 하지 않기 위해 다음에 한 번더 풀어봐야겠다.