[Algorithm] DP 동적계획법 (Dynamic Programming)

김민주·2022년 10월 7일
0

Algorithm

목록 보기
5/7

DP란?

: 큰 문제를 작은 문제로 나누어 그 답을 저장해두고 재활용하는 문제해결법

= 동적계획법


  • 조건
    1. 겹치는 부분이 있어야 한다. (메모이제이션으로 중복 계산 줄이기)
    2. 최적 부분 구조여야 한다. 부분문제의 최적결과값을 이용하여 전체의 최적 결과를 낼 수 있는 경우.

두 가지 조건을 만족하여야 DP로 풀 수 있는 환경이 된다.



메모이제이션 이란?

: 동일한 계산을 반복해야 할 경우 한 번 계산한 결과를 메모리에 저장해 두었다가 꺼내 씀

= 캐싱 (Caching)




DPDP 문제를 푸는데에 있어서 Top-down 방식과 Bottom-up 방식이 있다.

  1. Top-down : 가장 큰 문제를 풀고 작은 문제들을 호출하는 방식

  2. Bottom-up : 작은 문제들 부터 답을 찾아가는 방식


  • 구현

    Top-down : 재귀호출
    Bottom-up : 반복문




백준 DP 예제

24416번 - 피보나치 수

package DynamicProgramming;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class DpFibo {//24416

    static int[] f;
    static int dp = 0;
    static int recur = 0;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        f = new int[n];
        fibo(n);
        recurFibo(n);
        System.out.println(recur + " " + dp);
    }

    static int recurFibo(int n) {
		// TopDown
        if (n == 1 || n == 2) {
            recur++;
            return 1;
        } else {
            return (recurFibo(n - 1) + recurFibo(n - 2));
        }
    }


    static void fibo(int n) {
		// bottomUp
        if (n == 0 || n == 1) {
            f[n] = 1;
        }
        for (int i = 2; i < n; i++) {
            dp++;
            f[i] = f[i - 1] + f[i - 2];
        }
    }
}



1463번 - 1로 만들기

package DynamicProgramming;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Make1 {

    static Integer[] dp;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        dp = new Integer[n + 1];
        dp[1] = 0;


        for(int i=2; i<=n;i++){
            dp[i] = dp[i-1] +1;
            //1빼고 n-1 더한 것과 나눠지는 것에서 1더한 것 중 최솟값
            if(i%3 == 0){
                dp[i] = Math.min(dp[i], dp[i/3] +1);
            }
            if(i%2 == 0){
                dp[i] = Math.min(dp[i], dp[i/2] +1);
            }
        }

        System.out.println(dp[n]);
    }

}



11054번 - 가장 긴 바이토닉 부분 수열

package DynamicProgramming;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class LIS2 { //11054

    /*
    나열된 숫자를 가지고
    왼->오 방향 rdp,
    오->왼 방향 ldp 배열을 만든다.

    배열에는 앞서 자기 자신보다 작은 값을 담는다.

    rdp + ldp - 1(자기자신 중복) 값이 최대인 값을 출력한다.

     */

    static int[] arr;
    static int[] rdp;
    static int[] ldp;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());

        arr = new int[n + 1];
        rdp = new int[n + 1];
        ldp = new int[n + 1];

        //rdp[1] = 1; ldp[n] = 1;

        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= n; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
            //모두 1로 초기화
            rdp[i] = 1;
            ldp[i] = 1;
        }

        System.out.println(getDp(n));

    }

    public static int getDp(int n) {
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                if (arr[j] < arr[i] && rdp[j] >= rdp[i]) // rdp[j]가 나보다 작으면 안됨
                    rdp[i]++;
            }
        }
        for (int i = n - 1; i >= 1; i--) { //ldp[n]은 1
            for (int j = n; j > i; j--) {
                if (arr[j] < arr[i] && ldp[j]>=ldp[i])
                    ldp[i]++;
            }
        }
        int max = 0;
        int m = n+1;
        while(m-->1){
            max = Math.max(max, rdp[m]+ldp[m]-1);
        }
        return max;
    }
}



2156번 - 포도주 시식

package DynamicProgramming;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class GrapeWine { //2156


    static int[] arr;
    static int n;
    static int[] dp; //Bottomup 반복문
    static Integer[] dyp; //Topdown 재귀
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        arr = new int[n + 1];
        dp = new int[n + 1];

            dyp = new Integer[n+1];

        for (int i = 1; i <= n; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }
        dp[0] = 0; dyp[0] = 0;
        dp[1] =arr[1]; dyp[1] = arr[1];
        if(n>1) dp[2] = arr[1]+arr[2];
        if(n>1) dyp[2] = arr[1]+arr[2];
        for( int i=3;i<=n;i++) {
            dp[i] = Math.max(dp[i-1],Math.max(dp[i-3]+arr[i-1], dp[i-2])+arr[i]);
        }

        System.out.println(dp[n]);
        //System.out.println(recur(n));
    }



    public static int recur(int n){

        if(dyp[n] == null)
            dyp[n] = Math.max(Math.max(recur(n-2), recur(n-3)+arr[n-1])+arr[n], recur(n-1));
          return dyp[n];

    }
}
profile
𝐃𝐨𝐧'𝐭 𝐛𝐞 𝐚 𝐩𝐫𝐨𝐜𝐫𝐚𝐬𝐭𝐢𝐧𝐚𝐭𝐨𝐫💫

0개의 댓글