Open dlehdanakf opened 3 years ago
l
, m
, r
l ~ m
, m + 1 ~ r
까지일 때 최대합이 되는 구간을 구하고 그 값을 반환하면 된다.l 부터 m 까지
구간의 합은 PS[m] - PS[l]
이다.ㅣ ~ m
이라고 했는데 이는 l 번째 값을 포함하는 범위란거다. 즉, 엄밀히 말하면 PS[n] - PS[m - 1]
을 해줘야 m 을 포함한 구간합을 구할 수 있다.m - 1
을 할 때 만약 m 이 0이면 어떡하지? 그래서 첫 번째 원소를 0으로 두는 N 보다 원소의 개수가 한 개 많은 PS 배열을 만든다.function solution(cookie) {
// 부분합 배열 만들기
const PS = [0, ...cookie];
for(let i = 1; i < PS.length; i++) {
PS[i] += PS[i - 1];
}
let answer = 0;
for(let m = 0; m < PS.length; m++) {
const t = PS[m]; // 0 부터 m 까지 합
for(let r = m + 1; r < PS.length; r++) {
const right = PS[r] - t; // m + 1 부터 r 까지 합
if(t < right || right <= answer) {
// right 값이 answer 보다 작다면 계산해볼 필요가 없다.
// right 값이 t 보다 크다면 t에서 더 줄여봤자 항상 right 값이 더 크므로 더 계산할 필요가 없다.
continue;
}
for(let l = 0; l < m; l++) {
const left = t - PS[l]; // l ~ m 까지 합
if(left === right) {
// left 값과 right 값이 같다면 정답일 수 있다.
answer = Math.max(answer, left);
} else if(left < right) {
// left 값이 right 보다 작다면 더 이상 부분합 구간을 줄여봤자 항상 작으므로 l 탐색을 멈춘다.
break;
}
}
}
}
return answer;
}
l
, m
, r
세 점이 있을 때, l
부터 정하기 보다는 m
을 먼저 정하는게 유리하다. l
부터 정할 경우 중간중간 더 이상 볼 필요가 없는 조건에 대해 조건문을 만들기가 애매하지만, 어쨌든 구간은 m 을 기준으로 나뉘는 만큼 l
가 정해지지 않은 상태에서도 조건문을 삽입하여 시간을 단축시킬 수 있다.PS[j] - PS[i]
연산을 한다. 하지만 이게 말이 연산이지 실제로는 모든 보석의 목록을 한바퀴 도는 O(n)의 시간이 필요한 작업이다. 아마 여기서 시간초과가 날 것이다.0 <= j <= i <= N
인데 i 를 움직여서 먼저 모든 보석이 있는 구간부터 1 씩 증가한다.function solution(gems) {
const PS = new Array(gems.length + 1).fill(undefined);
PS[0] = new Map;
for(let i = 1; i < PS.length; i++) {
PS[i] = new Map(PS[i - 1]);
const gem = gems[i - 1];
PS[i].set(gem, (PS[i].get(gem) || 0) + 1);
}
const gem_categories = [...PS[PS.length - 1].keys()];
const gem_diff = (a, b) => {
for(const gem of gem_categories) {
if((b.get(gem) || 0) - (a.get(gem) || 0) < 1) {
return false;
}
}
return true;
};
let _i = PS.length, _j = 1, gap = _i - _j;
for(let i = gem_categories.length; i < PS.length; i++) {
if(PS[i].size !== gem_categories.length) {
continue;
}
for(let j = Math.max(i - gap, 1); j <= i; j++) {
const c = gem_diff(PS[j - 1], PS[i]);
if(c === true && i - j < gap) {
gap = i - j; _i = i; _j = j;
} else if(c === false) {
break;
}
}
}
return [_j, _i];
}
Array.prototype.slice
메소드를 활용해 코드를 작성해봤다.function solution(gems) {
const gem_set = new Set(gems);
const gem_size = (s, e) => (new Set(gems.slice(s, e))).size;
let is_verified = false, gap = gems.length, _i, _j;
for(let i = gem_set.size - 1; i < gems.length; i++) {
if(gem_size(0, i + 1) !== gem_set.size) {
continue;
}
for(let j = 0; j <= i; j++) {
const c = gem_size(j, i + 1) === gem_set.size;
if(c === true && i - j < gap) {
gap = i - j, _i = i, _j = j;
} else if(c === false) {
break;
}
}
}
return [_j + 1, _i + 1];
}
Map
객체를 사용한다.function solution(gems) {
const { size } = new Set(gems);
const obj = new Map;
let _i, _j;
gems.forEach((gem, i) => {
obj.delete(gem);
obj.set(gem, i);
if(obj.size === size) {
// const [ j ] = [...obj.values()];
const { value: j } = obj.values().next();
if(_j === undefined || _i - _j > i - j) {
_i = i;
_j = j;
}
}
});
return [_j + 1, _i + 1];
}
Map.prototype.delete
메소드를 굳이 호출하는 이유는 단순 set 작업만 반복할 경우 Map.prototype.values
메소드를 호출했을 때 값들의 순서가 바뀌지 않기 때문이다.gems[0]
만 찾을 수 있을 것이다. delete 메소드를 통해 "수정"이 아닌 "삭제 후 삽입"이 이루어져야한다.const [ j ] = [...obj.values()]
코드로 j 값을 가져오면 시간초과가 발생한다. 아무래도 MapIterator 객체에서 배열로 초기화하는 비용 때문인 것 같다. 보석의 종류가 정말 다양하다면 아무래도 이 작업 또한 시간이 제법 소요될 것이다.values
메소드를 호출했으면 불편하더라도 그냥 MapIterator 객체를 그대로 사용하자. 해당 객체를 스프레드 연산자로 배열화 하는 것도 시간효율성 측면에서 안좋다.
개요
예시
TapeEquilibrium
1, 2, ..., A-1
P에 대해 모든 경우에서 부분합을 구할 경우 아마 시간초과가 날 것이다. 부분합을 구하기 위해 계속O(n)
의 시간을 쓰기 때문해결과정
left
변수에, A2의 부분합을right
변수에 미리 저장한다.A[1]
값을 더해주고, right에는 빼준 뒤 두 부분합의 차이를 기록한다.