새소식

JAVA (개념, 알고리즘)

Dynamic Programming(동적계획법) - 기초

  • -

1. 계단 오르기 (피보나치)

철수는 계단을 오를 때 한 번에 한 계단 또는 두 계단씩 올라간다. 만약 총 4계단을 오른다면 그 방법의 수는

1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2 로 5가지이다.

그렇다면 총 N계단일 때 철수가 올라갈 수 있는 방법의 수는 몇 가지인가?

입력

첫째 줄은 계단의 개수인 자연수 N(3≤N≤35)이 주어집니다.

 

출력

첫 번째 줄에 올라가는 방법의 수를 출력합니다.

예시 입력 1 

7

예시 출력 1

21

[코드]

import java.util.*;

class Main {
    static int[] dy;
    public int solution(int n)
    {
        dy[1] = 1;
        dy[2] = 2;
        for(int i=3; i<=n; i++)
        {
            dy[i] = dy[i-2] + dy[i-1];
        }
        return dy[n];
    }
    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        dy = new int[n+1];
        System.out.println(T.solution(n));
    }
}

 

2. 돌다리 건너기

철수는 학교에 가는데 개울을 만났습니다. 개울은 N개의 돌로 다리를 만들어 놓았습니다.

철수는 돌 다리를 건널 때 한 번에 한 칸 또는 두 칸씩 건너뛰면서 돌다리를 건널 수 있습니다.

철수가 개울을 건너는 방법은 몇 가지일까요?

입력

첫째 줄은 돌의 개수인 자연수 N(3≤N≤35)이 주어집니다.

 

출력

첫 번째 줄에 개울을 건너는 방법의 수를 출력합니다.

예시 입력 1 

7

예시 출력 1

34

[코드]

import java.util.*;

class Main {
    static int[] dy;
    public int solution(int n)
    {
        dy[1] = 1;
        dy[2] = 2;
        for(int i=3; i<=n+1; i++)
        {
            dy[i] = dy[i-2] + dy[i-1];
        }
        return dy[n+1];
    }
    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        dy = new int[n+2];
        System.out.println(T.solution(n));
    }
}

 

3. 최대 부분 증가수열(LIS)

N개의 자연수로 이루어진 수열이 주어졌을 때, 그 중에서 가장 길게 증가하는(작은 수에서 큰 수로) 원소들의 집합을 찾는 프로그램을 작성하라.

예를 들어, 원소가 2, 7, 5, 8, 6, 4, 7, 12, 3 이면 가장 길게 증가하도록 원소들을 차례대로 뽑아내면 2, 5, 6, 7, 12를 뽑아내어 길이가 5인 최대 부분 증가수열을 만들 수 있다.

 

입력

첫째 줄은 입력되는 데이터의 수 N(3≤N≤1,000, 자연수)를 의미하고,

둘째 줄은 N개의 입력데이터들이 주어진다.

 

출력

첫 번째 줄에 부분증가수열의 최대 길이를 출력한다.

예시 입력 1 

8
5 3 7 8 6 2 9 4

예시 출력 1

4

<코드 - 주석 잘 읽어볼 것>

import java.util.*;

class Main {
    // dy 함수의 배열 값에 arr 배열 인덱스의 값을
    // 마지막 항으로 하는 최대 증가 수열의 길이를 저장한다.
    static int[] dy;
    public int solution(int[] arr)
    {
        int answer=0;
        dy = new int[arr.length];
        dy[0]=1;
        for(int i=1; i<arr.length; i++) {
            // 값이 없을 경우 아래에 max+1로 하여 1을 리턴받기 위해
            int max = 0;
            for(int j=i-1; j>=0; j--) {
                if (arr[j] < arr[i] && dy[j]>max)
                    max = dy[j];
            }
            dy[i] = max+1;
            // 최댓값이 answer에 저장된다.
            answer = Math.max(answer, dy[i]);
        }
        return answer;
    }
    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];

        for(int i=0; i<n; i++)
           arr[i] = sc.nextInt();

        System.out.println(T.solution(arr));
    }
}

 

4. 가장 높은 탑 쌓기 (출처 : 한국정보올림피아드)

밑면이 정사각형인 직육면체 벽돌들을 사용하여 탑을 쌓고자 한다.

탑은 벽돌을 한 개씩 아래에서 위로 쌓으면서 만들어 간다.

아래의 조건을 만족하면서 가장 높은 탑을 쌓을 수 있는 프로그램을 작성하시오.

 

(조건1) 벽돌은 회전시킬 수 없다. 즉, 옆면을 밑면으로 사용할 수 없다.

(조건2) 밑면의 넓이가 같은 벽돌은 없으며, 또한 무게가 같은 벽돌도 없다.

(조건3) 벽돌들의 높이는 같을 수도 있다.

(조건4) 탑을 쌓을 때 밑면이 좁은 벽돌 위에 밑면이 넓은 벽돌은 놓을 수 없다.

(조건5) 무게가 무거운 벽돌을 무게가 가벼운 벽돌 위에 놓을 수 없다.

 

입력

입력 파일의 첫째 줄에는 입력될 벽돌의 수가 주어진다. 입력으로 주어지는 벽돌의 수는 최대 100개이다.

둘째 줄부터는 각 줄에 한 개의 벽돌에 관한 정보인 벽돌 밑면의 넓이, 벽돌의 높이 그리고 무게가 차례대로 양의 정수로 주어진다.

각 벽돌은 입력되는 순서대로 1부터 연속적인 번호를 가진다. 벽돌의 넓이, 높이 무게는 10,000보다 작거나 같은 자연수이다.

 

출력

첫 번째 줄에 가장 높이 쌓을 수 있는 탑의 높이를 출력한다.

예시 입력 1 

5
25 3 4
4 4 6
9 2 3
16 2 5
1 5 2

예시 출력 1

10

[코드]

import java.util.*;
class Brick implements Comparable<Brick> {
    public int s, h, w;
    Brick(int s, int h, int w)
    {
        // 넓이 : s, 높이 : h, 무게 : w
        this.s = s;
        this.h = h;
        this.w = w;
    }
    @Override
    public int compareTo(Brick o) {
        return o.s - this.s; //내림차순 정렬
    }
}

class Main {
    static int[] dy; // 각 인덱스 배열 값이 최대 높이라고 생각했을 때 나올 수 있는
    				 // 벽돌의 수를 저장한다.
    public int solution(ArrayList<Brick> arr)
    {
        int answer=0;
        Collections.sort(arr); // 넓이에 의해서 내림차순 정렬
        dy[0] = arr.get(0).h; // 높이 값 저장
        answer = dy[0];
        for(int i=1; i<arr.size(); i++) {
            int max_h=0;
            for(int j=i-1; j>=0; j--) {
                if (arr.get(j).w > arr.get(i).w && dy[j] > max_h)
                {
                    max_h = dy[j];
                }
            }
            dy[i] = max_h + arr.get(i).h;
            answer = Math.max(answer, dy[i]);
        }

        return answer;
    }
    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);

        int n = sc.nextInt();
        ArrayList<Brick> arr =new ArrayList<>();
        dy = new int[n];

        for(int i=0; i<n; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            arr.add(new Brick(a,b,c));
        }
        System.out.println(T.solution(arr));
    }
}

 

5. 동전교환(냅색 알고리즘)

다음과 같이 여러 단위의 동전들이 주어져 있을때 거스름돈을 가장 적은 수의 동전으로 교환해주려면 어떻게 주면 되는가?

각 단위의 동전은 무한정 쓸 수 있다.

 

입력

첫 번째 줄에는 동전의 종류개수 N(1<=N<=50)이 주어진다.

두 번째 줄에는 N개의 동전의 종류가 주어지고, 그 다음줄에 거슬러 줄 금액 M(1<=M<=500)이 주어진다.

각 동전의 종류는 100원을 넘지 않는다.

 

출력

첫 번째 줄에 거슬러 줄 동전의 최소개수를 출력한다.

예시 입력 1 

3
1 2 5
15

예시 출력 1

3

힌트 : 5 5 5 동전 3개로 거슬러 줄 수 있다.

[코드]

dy[i] 의 값은 i라는 금액을 만드는데 필요한 최소 동전 개수를 말한다.

예 : dy[10]은 10원을 만드는데 필요한 최소 동전 개수를 의미한다.

coin[i] : 동전의 가격의 종류를 저장하는 배열.

import java.util.*;

class Main {
    static int n,m;
    static int[] dy;
    public int solution(int[] coin)
    {
        //dy를 Integer.MAX_VALUE로 모두 초기화
        Arrays.fill(dy, Integer.MAX_VALUE);
        dy[0] = 0;
        for(int i=0; i<n; i++) {
            for(int j=coin[i]; j<=m; j++) {
                dy[j] = Math.min(dy[j], dy[j-coin[i]]+1);
            }
        }
        return dy[m];
    }

    public static void main(String[] args) {
        Main T = new Main();
        Scanner sc = new Scanner(System.in);

        n = sc.nextInt();
        int[] arr = new int[n];
        for(int i=0; i<n; i++)
            arr[i] = sc.nextInt();

        m = sc.nextInt();
        dy = new int[m+1]; ;// 1부터 n까지의 배열을 저장하기 위해

        System.out.println(T.solution(arr));
    }
}

 

6. 최대점수 구하기(냅색 알고리즘)

이번 정보올림피아드대회에서 좋은 성적을 내기 위하여 현수는 선생님이 주신 N개의 문제를 풀려고 합니다.

각 문제는 그것을 풀었을 때 얻는 점수와 푸는데 걸리는 시간이 주어지게 됩니다.

제한시간 M안에 N개의 문제 중 최대점수를 얻을 수 있도록 해야 합니다.

(해당문제는 해당시간이 걸리면 푸는 걸로 간주한다, 한 유형당 한개만 풀 수 있습니다.)

 

입력

첫 번째 줄에 문제의 개수N(1<=N<=50)과 제한 시간 M(10<=M<=300)이 주어집니다.

두 번째 줄부터 N줄에 걸쳐 문제를 풀었을 때의 점수와 푸는데 걸리는 시간이 주어집니다.

 

출력

첫 번째 줄에 제한 시간안에 얻을 수 있는 최대 점수를 출력합니다.

예시 입력 1 

5 20
10 5
25 12
15 8
6 3
7 4

예시 출력 1

41

[코드]

dy의 인덱스 값은 제한 시간 값을 의미한다.

dy[j]의 배열 값은 j분 동안 받을 수 있는 최대 점수를 의미한다.

예 : dy[16] : 16분 안에 얻을 수 있는 최대 점수를 말한다.

이 문제는 j가 맨 뒤에서부터 시작해야 중복을 피할 수 있다. 

import java.util.*;

class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int[] dy = new int[m+1];

        for(int i=0; i<n; i++) {
            int ps = sc.nextInt();
            int pt = sc.nextInt();

            for(int j=m; j>=pt; j--) {
                //dy[j-pt]+ps 는 해당 문제를 풀었을 때를 의미한다.
                dy[j] = Math.max(dy[j], dy[j-pt]+ps);
            }
        }
        System.out.println(dy[m]);
    }
}

 

'JAVA (개념, 알고리즘)' 카테고리의 다른 글

StringBuilder 사용법 (StringBuffer, String과 비교)  (0) 2023.03.05
BufferedReader와 BufferedWriter  (0) 2023.03.05
그리디 알고리즘  (0) 2023.02.18
DFS, BFS 활용 (2)  (0) 2023.02.09
DFS, BFS 활용 (1)  (0) 2023.02.07
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.