[softeer] 그리디 & DP

세나정·2025년 2월 27일
post-thumbnail

(Lv.3) 효도음식 (Kadane's Algorithm)

  • Kadane's Algorithm 활용 : 연속된 부분 배열의 최대 합을 찾기 위한 효율적인 알고리즘 [O(n) 시간 복잡도]
  • 주로 배열이 주어졌을 때 그 안에서 가장 큰 연속 부분 배열의 합을 찾는 데 사용

풀이

// 그리디 - Kadane's Algorithm
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().trim().split('\n');

function solution() {
    const n = parseInt(input[0]);
    const foods = input[1].split(' ').map(Number);

    // 왼쪽부터 오른쪽으로 최대 부분합 계산 (Kadane's Algorithm)
    let maxLeft = new Array(n).fill(-Infinity);
    let sumSoFar = 0; // 현재까지의 부분합 
    let currentMax = -Infinity; // 최대 부분합
    
    for (let i = 0; i < n; i++) {
        // 이전의 부분합 + 현재 값 vs 새로운 시작점으로 현재 값
        sumSoFar = Math.max(foods[i], sumSoFar + foods[i]);

        // 현재까지의 최대값 갱신
        currentMax = Math.max(currentMax, sumSoFar);

        // 해당 i번째에 현쟤까지 최대값 삽입
        maxLeft[i] = currentMax;
    }

    console.log('maxLeft', maxLeft)
    
    // 오른쪽부터 왼쪽으로 최대 부분합 계산 (Kadane's Algorithm)
    let maxRight = new Array(n).fill(-Infinity);
    currentMax = -Infinity;
    sumSoFar = 0;
    
    for (let i = n - 1; i >= 0; i--) {
        sumSoFar = Math.max(foods[i], sumSoFar + foods[i]); // 새 시작점 고려
        currentMax = Math.max(currentMax, sumSoFar);
        maxRight[i] = currentMax;
    }
    
    // 두 요리를 만들 때 최대 만족도 찾기
    let maxValue = -Infinity;
    for (let i = 0; i < n - 1; i++) {
        if (i + 2 < n) {
            maxValue = Math.max(maxValue, maxLeft[i] + maxRight[i + 2]);
        }
    }

    console.log('maxRight', maxRight)
    
    // 최종 결과 출력
    console.log(maxValue);
}

solution();

(Lv.3) 징검다리 (LIS)

  • 최장 증가 부분 수열 문제 (LIS, Longest Increasing Subsequence) : 주어진 수열에서 일부 원소를 제거하여 얻을 수 있는 가장 긴 "오름차순 부분 수열"의 길이를 구하는 문제

여기에서 배운점

예시를 들 때, 템포가 느린 순의 음악만을 순차적으로 넣는다고 생각하면 됨

풀이

const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().trim().split('\n');

function solution() {
    const N = parseInt(input[0])
    const stones = input[1].split(' ').map(Number);

    let dp = new Array(N).fill(1); // 최장증가 부분 수열의 길이를 저장할 dp

    for (let i = 1; i < N; i++) {
        for (let j = 0; j < i; j++) {
            // 순회중인(i)가 바로 직전 애 (j)보다 크다면
            if (stones[j] < stones[i]) {  
                // 길이가 저장된 dp 배열에다가 수열의 길이 +1 (더 길어질 수있게)
                // 한번 증가한 후로는 어차피 if문을 통과하지 못하기 떄문에 길이는 변하지 않음

                // 모든 j 중에서 가장 긴 lis를 선택해야 하므로
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
    }

    console.log(Math.max(...dp)) 
}

solution()

나무 수확

풀이

const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().trim().split('\n');

function solution() {
    const n = parseInt(input[0]);
    const grid = input.slice(1).map(line => line.split(' ').map(Number));

    // DP 배열 초기화
    let dp = Array.from({ length: n }, () =>
        Array.from({ length: n }, () => [0, 0]) // dp[i][j][0]: 스프링클러X, dp[i][j][1]: 스프링클러O
    );

    // 시작점 초기화
    dp[0][0][0] = grid[0][0];
    dp[0][0][1] = grid[0][0] * 2;

    // 첫 번째 행 (오른쪽으로 이동만 가능)
    for (let j = 1; j < n; j++) {
        dp[0][j][0] = dp[0][j - 1][0] + grid[0][j];
        dp[0][j][1] = Math.max(dp[0][j - 1][1] + grid[0][j], dp[0][j - 1][0] + grid[0][j] * 2);
    }

    // 첫 번째 열 (아래쪽으로 이동만 가능)
    for (let i = 1; i < n; i++) {
        dp[i][0][0] = dp[i - 1][0][0] + grid[i][0];
        dp[i][0][1] = Math.max(dp[i - 1][0][1] + grid[i][0], dp[i - 1][0][0] + grid[i][0] * 2);
    }

    // DP 배열 채우기 (우 →, 하 ↓ 중 큰 값 선택)
    for (let i = 1; i < n; i++) {
        for (let j = 1; j < n; j++) {
            // 스프링클러 미사용
            dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i][j - 1][0]) + grid[i][j];

            // 스프링클러 사용 (현재 위치에서 처음 사용 or 기존에 사용)
            dp[i][j][1] = Math.max(
                dp[i - 1][j][1] + grid[i][j], // 위쪽에서 스프링클러 사용한 경우
                dp[i][j - 1][1] + grid[i][j], // 왼쪽에서 스프링클러 사용한 경우
                dp[i - 1][j][0] + grid[i][j] * 2, // 현재 위치에서 처음 사용
                dp[i][j - 1][0] + grid[i][j] * 2  // 현재 위치에서 처음 사용
            );
        }
    }

    // 최댓값 출력
    console.log(dp[n - 1][n - 1][1]); // (n-1, n-1) 위치에서 스프링클러 사용한 최대 수확량
}

solution();
profile
기록, 꺼내 쓸 수 있는 즐거움

0개의 댓글