문제 링크

분류

너비 우선 탐색, 그래프 이론, 그래프 탐색

문제 설명

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다.

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

 

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

 

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

 

풀이 (BFS 너비 우선 탐색)

시간 복잡도 O(n*m) 공간 복잡도 O(n*m)

아이디어: 저장된 모든 토마토의 (상하좌우)주변의 토마토가 익는 과정을 1사이클 이라고 정의 할 때

익은 토마토들의 정보를 가지고 사이클을 돌리고 이때 익게된 토마토들의 위치 정보를 모두 다음 사이클에 사용한다. 

만약 다음 사이클에 사용할 정보가 비어 있다면 반복을 멈추고 최종적으로 모든 격자를 검사했을 때 0이 남아 있는지 체크 후 

있다면 -1 없다면 지금까지 반복한 사이클의 수를 출력한다.

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

public class Main {
	// 위치 정보를 저장할 클래스
    static class Pos{
        int x;
        int y;
        public Pos(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    static Pos[] ways = {new Pos(1,0),new Pos(0,1),
            new Pos(-1,0),new Pos(0,-1)};
    // 격자에 0이 있는지 판단하는 함수
    public static boolean judge(int[][] box){
        for (int[] ints : box) {
            for (int anInt : ints) {
               if(anInt==0)
                   return false;
            }
        }
        return true;
    }
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int m = Integer.parseInt(st.nextToken());
        int n = Integer.parseInt(st.nextToken());
        ArrayList<Pos> sp = new ArrayList<>();

        Queue<ArrayList<Pos>> queue = new LinkedList<>();
        int[][] box = new int[n][m];
        // ArrayList<Pos> startPoint = new ArrayList<>();

        for (int i = 0; i <n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j <m ; j++) {
                box[i][j] = Integer.parseInt(st.nextToken());
                if(box[i][j]==1) sp.add(new Pos(j,i));
            }
        }
        
        // BFS 작업
        queue.add(sp);

        int ans = 0;
        while(!queue.isEmpty()){
            ArrayList<Pos> node = queue.poll();
            ArrayList<Pos> nextNode = new ArrayList<>();
            for (Pos cur : node) {
                for (Pos way : ways) {
                    int x = cur.x+way.x;
                    int y = cur.y+way.y;
                    if(x<0||y<0||x>=m||y>=n)
                        continue;
                    if(box[y][x]==0){
                        box[y][x] = 1;
                        nextNode.add(new Pos(x,y));
                    }
                }
            }
            if(!nextNode.isEmpty())
                queue.add(nextNode);
            ans++;
        }

        if(judge(box))
            System.out.println(ans-1);
        else
            System.out.println(-1);
    }
}

'PS > Gold' 카테고리의 다른 글

[Gold V] 토마토 - 7569 (Java)  (0) 2023.08.15
[Gold IV] 숨바꼭질 4 - 13913 (Java)  (0) 2023.08.04
[Gold III] Happy Cow - 13002 (Java)  (0) 2023.07.29
[Gold III] 수확 - 1823 (Java)  (0) 2023.07.29
[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29

문제 링크

 

분류

다이나믹 프로그래밍

 

문제 설명

천나라에 살고있는 민호는 애지중지하는 소 한마리가 있다. 소의 행복은 곧 민호의 행복이기 때문에 가지고 있는 전재산을 털어 최고로 맛있는 여물 N개를 구매 했다.

민호는 여물을 관리하기 쉽게 폭은 좁지만 너비가 매우 긴 창고에 차례대로 넣었고, 왼쪽부터 한개씩 1번, 2번, 3번, ... , N번 여물이라 부르기로 했다. 각각의 여물들은 소가 먹었을 때 느끼는 행복이 다를수가 있다. 예를 들어 1번 여물을 먹으면 소가 느끼는 행복은 20이지만 2번 여물을 먹으면 소가 느끼는 행복은 10일수가 있다는 것이다. 이를 편의상 행복도라 부르기로 하자.

그리고 여물들은 날이 지날수로 숙성이 되어 맛이 더 좋아져 행복도가 올라간다. 이는 (행복도 * 여물을 구매한 뒤 지난 날자)로 계산할 수 있다. 여물을 구매한 뒤 지난 날자는 구매한 당일의 날이 1이고 그 다음날이 2라고 계산한다.

이것들을 백만년 숙성시킨 뒤 소에게 주면 소는 극락을 맛보겠지만 그때까지 기다릴 수 없던 민호는 여물을 구매한 날부터 N번째 날까지, 하루에 한 개의 여물을 소에게 주기로 하였다. 하지만 폭이 좁고 너비가 매우 긴 창고에 여물을 저장했기 때문에 중간에 있는 여물을 꺼내는건 불가능 하고 왼쪽과 오른쪽, 양쪽 끝에서 하나만 꺼내 소에게 주는 것이 가능하다. 즉 i ≤ j (1 ≤ i ≤ j ≤ N) 의 여물이 남아 있다면 민호는 i번 여물이나 j번 여물을 선택을 해서 소에게 줘야 한다는 뜻이다. i또는 j외의 여물을 소에게 줄 수 없다.

민호는 소가 최대로 높은 행복을 느꼇으면 좋겠다고 생각 한다. N일에 걸쳐 모든 여물을 소에게 줬을 때 소가 느끼는 행복도의 합이 최대가 되는 경우를 구해주자.

 

입력

첫 번째 줄에 여물의 개수 N (1 ≤ N ≤ 2000) 이 주어진다.

두 번째 줄에 여물의 행복도 Hi (1 ≤ Hi ≤ 1000, 1 ≤ i ≤ N) 이 공백을 구분으로 N개 주어진다.

 

출력

N일에 걸쳐 모든 여물을 소에게 줬을 때 소가 느낄 수 있는 행복도들의 합중 최대를 출력한다.

 

풀이 (동적 프로그래밍)

시간 복잡도 O(n^2) 공간 복잡도 O(n^2)

아이디어: [Gold III] 수확 - 1823 (Java) 문제와 동일

https://suhanlim.tistory.com/269

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[][] dp = new long[n+1][n+1];
        int[] array = new int[n+1];
        dp[0][0] = 0;
        // 가치: array[i]*i
        
        StringTokenizer st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= n; i++) {
            array[i] = Integer.parseInt(st.nextToken());
        }

        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + (long) array[i] * i;
            dp[0][i] = dp[0][i-1] + (long)array[n-i+1]*i;
        }

        long ans = 0;
        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <=n; j++) {
                if(i+j>n) continue;
                dp[i][j] =Math.max(dp[i][j],dp[i-1][j]+ (long) (i + j) *array[i]);
                dp[i][j] =Math.max(dp[i][j],dp[i][j-1]+(long) (i+j)*array[n-j+1]);

                if(i+j==n)
                    ans = Math.max(ans,dp[i][j]);
            }
        }
        if(n==1)
            System.out.println(array[n]);
        else System.out.println(ans);
    }
}

 

'PS > Gold' 카테고리의 다른 글

[Gold IV] 숨바꼭질 4 - 13913 (Java)  (0) 2023.08.04
[Gold V] 토마토 - 7576 (Java)  (0) 2023.08.02
[Gold III] 수확 - 1823 (Java)  (0) 2023.07.29
[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29
[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27

문제 링크

분류

다이나믹 프로그래밍

 

문제 설명

1 × N 크기의 긴 밭에 벼가 심어져 있다. 준희는 이 벼를 수확 하려고 한다. 그런데 가운데 있는 벼를 수확을 하려면 끝에서 가운데까지 헤집고 들어가야 하므로 양 끝에 있는 벼만 수확을 할 수 있다. 처음에는 첫 번째와 N번째 벼를 수확을 할 수 있을 것이며 만약에 첫 번째 벼를 수확을 하였다면 두 번째 벼와 N번째 벼를 수확을 할 수 있다.

수확을 하였을 때 얻을 수 있는 이익은 다음과 같다. 만약에 그 벼의 가치가 v(i)라고 하고 그 벼를 k번째로 수확을 한다고 하면 v(i) × k 만큼의 이익을 얻게 된다.

만약에 벼의 가치가 차례로 1 3 1 5 2 라고 하고 첫 번째, 다섯 번째, 두 번째, 세 번째, 네 번째의 순서대로 수확을 한다고 하면 1×1 + 2×2 + 3×3 + 4×1 + 5×5 = 43 이 되어 43 만큼의 이익을 얻게 된다. 그리고 이 값이 저 벼로 얻을 수 있는 최대 이익이 된다.

우리가 구해야 할 값은 다음과 같다. 벼의 개수 N과 벼의 가치가 주어져 있을 때, 얻을 수 있는 최대 이익을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 벼의 개수 N(1 ≤ N ≤ 2,000)이 주어지고 두 번째 줄부터 N+1번쨰 줄까지 벼의 가치 v(i) (1 ≤ v(i) ≤ 1,000) 가 주어진다.

 

출력

첫째 줄에 얻을 수 있는 최대 이익을 출력한다.

 

실패한 풀이 (원인: 세밀하게 관리하지 못 한 DP 테이블)

시간 복잡도 O(n) 공간 복잡도 O(n)

아이디어: dp[n] 을 n번째 벼까지 수확 했을 때의 최대 값을 저장하는 공간으로 저장한다, dp[0] = 0 이다.

맨 처음과 맨 마지막 중에서 더 작은 값을 선택해서 수확한다. 만약 두 개의 값이 동일 하다면 작은 값이 더 가까운 쪽으로 수확한다.

모든 경우를 체크하지 못 하여 더 세밀한 DP 테이블 관리가 필요했다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    static boolean judge(int[] array,int start, int end){
        while(start<=end&&array[start]==array[end]){
            if(array[start]>array[end])
                return true;
            else if(array[end]>start)
                return false;
            start++; end--;
        }
        return true;
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[] dp = new long[n+1];
        int[] array = new int[n+1];
        // 가치: array[i]*i

        for (int i = 1; i <= n; i++) {
            array[i] = Integer.parseInt(br.readLine());
        }
        boolean way = true;

        int count = 1;
        int start = 1; int end = n;
        while(start<=end){
            if(array[start]==array[end]){
                way = judge(array,start+1,end-1);
                if(way)
                    dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[end--] *count);
                else
                    dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[start++] *count);
            }
           else if(array[start]>array[end])
               dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[end--] *count);
           else
               dp[count] = Math.max(dp[count],dp[count-1]+ (long) array[start++] *count);
           count++;
        }
        System.out.println(dp[n]);
    }
}

성공한 풀이 (동적 프로그래밍)

시간 복잡도 O(n^2) 공간 복잡도 O(n^2)

<아이디어>

1 단계: DP 테이블 설정

  • dp[i][j]는 제일 왼쪽 끝에서 i번 칸 만큼 수확하고 제일 오른쪽 끝에서 j번 칸 만큼 수확하여 얻은 작물의 최대 가치
  • i+j <= n 인 경우에만 성립 그 외의 공간은 관리 x (아래는 사진 예시)

2 단계: 초기 테이블 세팅

3 단계: DP 테이블을 갱신하는 규칙을 찾아 로직을 구현

제일 위에 예시와 마찬가지로 벼에 대한 가치의 배열이 (인덱스 1~n) {1, 2, 3, 4, 5) 일 때

왼쪽으로 2번, 오른쪽으로 1번 수확한 상황에서 추가로 4번째 작물을 수확해야 하는 경우 갱신 방법

  • 왼쪽의 벼를 추가로 수확 하는 경우
    • dp[3][1] = dp[2][1]+(3 + 1) *array[3];
  • 오른쪽의 벼를 추가로 수확 하는 경우
    • dp[2][2] = dp[2][1]+(2+2)*array[n-2+1];

4 단계: 목표하고자 하는 내용을 출력

모든 벼를 수확했을 때 최대값을 출력해야 하므로 i+j == n 인 경우의 값 중 최고값을 출력

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        long[][] dp = new long[n+1][n+1];
        int[] array = new int[n+1];
        dp[0][0] = 0;
        // 가치: array[i]*i
        for (int i = 1; i <= n; i++) {
            array[i] = Integer.parseInt(br.readLine());
        }
		// 초기 테이블 세팅
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + (long) array[i] * i;
            dp[0][i] = dp[0][i-1] + (long)array[n-i+1]*i;
        }
		// 로직
        long ans = 0;
        for (int i = 1; i <=n; i++) {
            for (int j = 1; j <=n; j++) {
                if(i+j>n) continue;
                dp[i][j] =Math.max(dp[i][j],dp[i-1][j]+ (long) (i + j) *array[i]);
                dp[i][j] =Math.max(dp[i][j],dp[i][j-1]+(long) (i+j)*array[n-j+1]);

                if(i+j==n)
                    ans = Math.max(ans,dp[i][j]);
            }
        }
        // 출력
        if(n==1)
            System.out.println(array[n]);
        else System.out.println(ans);
    }
}

 

 

 

 

 

'PS > Gold' 카테고리의 다른 글

[Gold V] 토마토 - 7576 (Java)  (0) 2023.08.02
[Gold III] Happy Cow - 13002 (Java)  (0) 2023.07.29
[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29
[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27
[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25

문제 링크

분류

다이나믹 프로그래밍

문제 설명

해빈이는 짜장면을 정말 좋아한다. 짜장면을 너무 좋아한 나머지 짜장면만 파는 중국집을 개업했다! 해빈이는 양손잡이여서 동시에 두 개의 웍(중국 냄비)을 사용하여 요리할 수 있다. 그러나 해빈이는 낭비를 매우 싫어하기 때문에 요리 할 때, 필요 이상 크기의 웍을 사용하지 않으며, 주문 받은 짜장면의 그릇 수에 딱 맞게 요리한다.

<웍>

예를 들어 짜장면 4그릇을 주문 받았는데 5그릇 이상을 요리하지 않으며, 4그릇을 요리할 수 있는 웍에 3그릇 이하의 요리를 하지 않는다.

해빈이가 5그릇을 주문 받았고, 해빈이가 가지고 있는 웍의 종류가 1, 3그릇 용이라면 처음에 1,3그릇용 웍을 동시에 이용하여 4그릇을 만들고 다음 1그릇용 웍을 이용하여 1그릇을 만들어 총 5그릇을 두 번의 요리로 만들 수 있다.

해빈이가 주문 받은 짜장면의 수와 가지고 있는 웍의 크기가 주어질 때, 최소 몇 번의 요리로 모든 주문을 처리할 수 있는지 출력하는 프로그램을 작성하시오.

 

입력

첫 번째 줄에는 해빈이가 주문 받은 짜장면의 수N(1≤N≤10,000)과 가지고 있는 웍의 개수 M(1≤M≤100)이 주어진다. 두 번째 줄에는 웍의 크기 Si(1≤Si≤N)이 M개가 주어지며 같은 크기의 웍을 여러 개 가지고 있을 수 있다.

 

출력

해빈이가 모든 주문을 처리하기 위해 해야 하는 최소 요리 횟수를 출력한다. 만약 모든 주문을 처리 할 수 없는 경우 -1을 출력한다.

 

풀이 (동적 프로그래밍)

시간 복잡도 O(n^3) 공간 복잡도 O(n)

아이디어: dp[n] 테이블을 n개의 주문을 최소한으로 해치우는 가짓수를 저장, 웍은 최대 2종류만 사용 가능하다는 점을 이용

dp[i] = dp[i-work]+1 의 관계식을 이용 (work는 한번에 웍을 돌릴 때 만들 수 있는 요리의 갯수)

우리는 웍을 개별로 사용한 경우와 두 개의 웍을 같이 사용한 경우를 체크하여서 경우를 체크해야한다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }

    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());
        long[] dp = new long[10001];
        Arrays.fill(dp,1000000);
        dp[0] = 0;
        int[] array = new int[k];

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < k; i++) {
            array[i] = Integer.parseInt(st.nextToken());
        }

        for (int i = 0; i < k; i++) {
            int op1 = array[i];
            for (int j = i+1; j < k; j++) {
                int op2 = array[j];
                for (int l = 1; l <= n; l++) {
                    int sum = op1+op2;
                    if(l-op1>=0) dp[l] = Math.min(dp[l],dp[l-op1]+1);
                    if(l-op2>=0) dp[l] = Math.min(dp[l],dp[l-op2]+1);
                    if(l-sum>=0) dp[l] = Math.min(dp[l],dp[l-sum]+1);
                }
            }
        }

        if (dp[n]>=1000000) {
            System.out.println(-1);
        } else {
            System.out.println(dp[n]);
        }
    }
}
if(l-op1>=0) dp[l] = Math.min(dp[l],dp[l-op1]+1);
if(l-sum>=0) dp[l] = Math.min(dp[l],dp[l-sum]+1);

위에 방식으로 사용하지 않는 이유는 

웍의 크기가 [2, 3]이고 주문된 짜장면의 양이 7이라고 가정할 때

위에 방식의 경우, 왹의 합은 2+2, 2+3, 3+3 의 경우만 체크하여 4,5,6 7을 만들기 위한 방법을 구할 수 없는 반면

첫 뻔재 코드의 방식의 경우 웍의 모든 가능한 조합을 고려 할 수 있기 때문이다.

 

풀이 (동적 프로그래밍)

시간 복잡도 O(n^2) 공간 복잡도 O(n)

아이디어: dp[n] 테이블을 n개의 주문을 최소한으로 해치우는 가짓수를 저장, 웍은 최대 2종류만 사용 가능하다는 점을 이용

미리 웍의 조합을 사용하여 음식을 만드는 경우를 저장하고 저장된 공간을 통해 최신화

 

j <= i/2로 설정하는 이유는, j와 i-j가 바뀐 경우는 이미 계산되었기 때문 예를 들어, i가 6일 때, j가 2인 경우와 j가 4인 경우는 사실상 같은 경우이다. (2+4와 4+2는 같은 것으로 취급). 따라서 j를 i/2까지만 반복해도 모든 경우를 고려할 수 있다.

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        int[] dp = new int[N+1];
        int[] arr = new int[M];
        Arrays.fill(dp, 1_000_000);  // dp 배열을 최대 값으로 초기화한다.

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < M; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        // 동적 프로그래밍을 수행한다.
        for (int i = 0; i < M; i++) {
            for (int j = i + 1; j < M; j++) {
                if (arr[i] + arr[j] <= N) {
                    dp[arr[i] + arr[j]] = 1;
                }
            }
            dp[arr[i]] = 1;
        }
        for (int i = 2; i <= N; i++) {
            for (int j = 0; j <= i / 2; j++) {
                dp[i] = Math.min(dp[i], dp[j] + dp[i-j]);
            }
        }

        // 모든 주문을 처리할 수 없는 경우 -1을 출력한다.
        if (dp[N] >= 1_000_000) {
            System.out.println(-1);
        } else {
            System.out.println(dp[N]);
        }
    }
}

 

'PS > Gold' 카테고리의 다른 글

[Gold III] Happy Cow - 13002 (Java)  (0) 2023.07.29
[Gold III] 수확 - 1823 (Java)  (0) 2023.07.29
[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27
[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25
[Gold V] 퇴사 2 - 15486 (Java)  (0) 2023.07.24

문제 링크

분류

다이나믹 프로그래밍

 

문제 설명

상근이와 선영이가 다른 사람들이 남매간의 대화를 듣는 것을 방지하기 위해서 대화를 서로 암호화 하기로 했다. 그래서 다음과 같은 대화를 했다.

  • 상근: 그냥 간단히 암호화 하자. A를 1이라고 하고, B는 2로, 그리고 Z는 26으로 하는거야.
  • 선영: 그럼 안돼. 만약, "BEAN"을 암호화하면 25114가 나오는데, 이걸 다시 글자로 바꾸는 방법은 여러 가지가 있어.
  • 상근: 그렇네. 25114를 다시 영어로 바꾸면, "BEAAD", "YAAD", "YAN", "YKD", "BEKD", "BEAN" 총 6가지가 나오는데, BEAN이 맞는 단어라는건 쉽게 알수 있잖아?
  • 선영: 예가 적절하지 않았네 ㅠㅠ 만약 내가 500자리 글자를 암호화 했다고 해봐. 그 때는 나올 수 있는 해석이 정말 많은데, 그걸 언제 다해봐?
  • 상근: 얼마나 많은데?
  • 선영: 구해보자!

어떤 암호가 주어졌을 때, 그 암호의 해석이 몇 가지가 나올 수 있는지 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 5000자리 이하의 암호가 주어진다. 암호는 숫자로 이루어져 있다.

 

출력

나올 수 있는 해석의 가짓수를 구하시오. 정답이 매우 클 수 있으므로, 1000000으로 나눈 나머지를 출력한다.

암호가 잘못되어 암호를 해석할 수 없는 경우에는 0을 출력한다.

 

풀이 (동적 프로그래밍 바텀 업)

시간 복잡도 O(n) 공간 복잡도 O(n)

아이디어: dp[i] " 번째 문자부터 i번째 문자까지의 부분 문자열을 해석하는 방법의 "라고 정의

현재의 문자가 하나의 문자로 해석될 수 있다면 dp[i] += dp[i-1] 

이전 문자까지 포함하여 하나의 문자로 해석될 수 있다면 dp[i] +=dp[i-2]

단 주의할 점은 문제에도 나와 있든 %1000000을 해주는 작업을 수시로 해주어야 한다. 

 

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    static int MOD = 1000000;
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String code = br.readLine();
        int n = code.length();
        int[] dp = new int[n+1];
        dp[0] = 1;

        for (int i = 1; i <= n; i++) {
            int head = code.charAt(i-1)-'0';
            if(head>=1 && head<=9){
                dp[i] +=dp[i-1];
                dp[i]%= MOD;
            }
            if(i==1) continue;
            int tail = code.charAt(i-2)-'0';
            if(tail==0) continue;
            int curr = tail*10+head;

            if(curr>=10&&curr<=26){
                dp[i]+=dp[i-2];
                dp[i]%= MOD;
            }
        }
        System.out.println(dp[n]);
    }
}

'PS > Gold' 카테고리의 다른 글

[Gold III] 수확 - 1823 (Java)  (0) 2023.07.29
[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29
[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25
[Gold V] 퇴사 2 - 15486 (Java)  (0) 2023.07.24
[Gold V] 호텔 - 1106 (Java)  (0) 2023.07.23

문제 링크

분류

다이나믹 프로그래밍

문제 설명

두 전봇대 A와 B 사이에 하나 둘씩 전깃줄을 추가하다 보니 전깃줄이 서로 교차하는 경우가 발생하였다. 합선의 위험이 있어 이들 중 몇 개의 전깃줄을 없애 전깃줄이 교차하지 않도록 만들려고 한다.

예를 들어, < 그림 1 >과 같이 전깃줄이 연결되어 있는 경우 A의 1번 위치와 B의 8번 위치를 잇는 전깃줄, A의 3번 위치와 B의 9번 위치를 잇는 전깃줄, A의 4번 위치와 B의 1번 위치를 잇는 전깃줄을 없애면 남아있는 모든 전깃줄이 서로 교차하지 않게 된다.

< 그림 1 >

전깃줄이 전봇대에 연결되는 위치는 전봇대 위에서부터 차례대로 번호가 매겨진다. 전깃줄의 개수와 전깃줄들이 두 전봇대에 연결되는 위치의 번호가 주어질 때, 남아있는 모든 전깃줄이 서로 교차하지 않게 하기 위해 없애야 하는 전깃줄의 최소 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 두 전봇대 사이의 전깃줄의 개수가 주어진다. 전깃줄의 개수는 100 이하의 자연수이다. 둘째 줄부터 한 줄에 하나씩 전깃줄이 A전봇대와 연결되는 위치의 번호와 B전봇대와 연결되는 위치의 번호가 차례로 주어진다. 위치의 번호는 500 이하의 자연수이고, 같은 위치에 두 개 이상의 전깃줄이 연결될 수 없다.

출력

첫째 줄에 남아있는 모든 전깃줄이 서로 교차하지 않게 하기 위해 없애야 하는 전깃줄의 최소 개수를 출력한다.

풀이: 동적 프로그래밍(LCS)

시간 복잡도 O(n^2) 공간 복잡도 O(n)

아이디어: 전봇대 A의 값들을 정렬 시키고 순차적으로 작업을 한다고 가정하면 전봇대 B에 대응하는 값이 순차적으로 커지는

ex: 1->2, 2->3, 3->4, 4->5 와 같은 경우에는 겹치는 선이 존재하지 않고 가장 이상적인 상황이다.

하지만 B에 대응하는 값이 1->3, 2->2 3->5, 4->7, 5->1 과 같이 중간에 순차적으로 이전 값 에서 증가하지 않고 갑자기 값이 작아지는 경우 겹치는 선이 생성되게 된다. 

위에 상황에서는 2->2, 5->1 의 전깃줄을 제거한다면

B에 해당 하는 값이 순차적으로 3->5->7로 커지는 연속적인 수열이 되어 선이 겹치지 않게 된다.

즉 2개의 선을 제거하여 겹치는 선이 없도록 만들었다고 볼 수 있다. (전체 길이 5 - 연속적인 최대 수열의 길이 3) = 2  

즉 이를 통해 얻을 수 있는 결론은 해당 문제가 요구하는 사항은 (전체 전깃줄의 갯수 - B에 대응하는 값에서 연속적인 최대 수열의 길이)를 출력하는 것 과 동일함을 알 수 있다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        int n = Integer.parseInt(br.readLine());
        int[] dp = new int[n+1];
        Pair[] info = new Pair[n+1];
        int ans=0;
        for (int i = 1; i <= n; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            info[i] = new Pair(a,b);
        }
        // 이 문제를 '가장 긴 증가하는 부분 수열' 문제로 바꾸어 해결하려 한다.
        // 전봇대 A에 연결된 전깃줄을 위치별로 정렬시 B에 연결된 전깃줄의 위치는 증가, 감소 또는 무작위 순서일 수 있다.
        // 전깃줄들이 교차하지 않도록 하기 위해서는 B에서 전깃줄이 연결된 위치가 증가하는 순서대로 정렬 되어 있어야 한다.
        Arrays.sort(info,1,n+1);
        // 따라서 우리는 가장 긴 증가하는 부분 수열을 찾아서 그 부분 수열에 포함되지 않은 전깃줄들을 제거하여야 한다.
        // 전깃줄 B에 숫자가 연속적이라면 아무 문제 없지만 갑자기 작아지게 되면 겹치기 때문에
        // 증가하는 수열에서 중간에 값이 작아지는 구간들을 찾아서 그 갯수를 반환해야 한다.
        // 아래는 증가하는 가장 긴 부분 수열을 찾는 과정
        int max = 0;
        for (int i = 1; i <= n; i++) {
            dp[i] = 1;
            for (int j = 1; j < i; j++) {
                // 만약 이전에 전봇대 A에서 연결된 B의 선의 값이 현재의 연결된 B의 값 보다 작다면 연속된 수열이라는 뜻
                // 우리의 목적은 해당 인덱스 까지 진행 했을 때 가장 긴 부분 수열의 길이임으로 dp[i] = dp[j]+1 을 이용하여 최신화
                if(info[j].b<info[i].b&&dp[i]<dp[j]+1)
                    dp[i] = dp[j]+1;
            }
            if(max < dp[i]) max = dp[i];
        }
        System.out.println(n-max);

    }
    static class Pair implements Comparable<Pair>{
        int a,b;
        public Pair(int a, int b){
            this.a = a;
            this.b = b;
        }
        @Override
        public int compareTo(Pair o){
            if(this.a>o.a){
                return 1;
            } else if(this.a == o.a){
                if(this.b>o.b){
                    return 1;
                }
            }
            return -1;
        }
    }
}

'PS > Gold' 카테고리의 다른 글

[Gold V] 개업 - 13910 (Java)  (0) 2023.07.29
[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27
[Gold V] 퇴사 2 - 15486 (Java)  (0) 2023.07.24
[Gold V] 호텔 - 1106 (Java)  (0) 2023.07.23
[Gold III] 우주선 만들기 - 15912 (Java)  (0) 2023.07.22

문제 링크

분류

다이나믹 프로그래밍

문제 설명

상담원으로 일하고 있는 백준이는 퇴사를 하려고 한다.

오늘부터 N+1일째 되는 날 퇴사를 하기 위해서, 남은 N일 동안 최대한 많은 상담을 하려고 한다.

백준이는 비서에게 최대한 많은 상담을 잡으라고 부탁을 했고, 비서는 하루에 하나씩 서로 다른 사람의 상담을 잡아놓았다.

각각의 상담은 상담을 완료하는데 걸리는 기간 Ti와 상담을 했을 때 받을 수 있는 금액 Pi로 이루어져 있다.

N = 7인 경우에 다음과 같은 상담 일정표를 보자.

  1일 2일 3일 4일 5일 6일 7일
Ti 3 5 1 1 2 4 2
Pi 10 20 10 20 15 40 200

1일에 잡혀있는 상담은 총 3일이 걸리며, 상담했을 때 받을 수 있는 금액은 10이다. 5일에 잡혀있는 상담은 총 2일이 걸리며, 받을 수 있는 금액은 15이다.

상담을 하는데 필요한 기간은 1일보다 클 수 있기 때문에, 모든 상담을 할 수는 없다. 예를 들어서 1일에 상담을 하게 되면, 2일, 3일에 있는 상담은 할 수 없게 된다. 2일에 있는 상담을 하게 되면, 3, 4, 5, 6일에 잡혀있는 상담은 할 수 없다.

또한, N+1일째에는 회사에 없기 때문에, 6, 7일에 있는 상담을 할 수 없다.

퇴사 전에 할 수 있는 상담의 최대 이익은 1일, 4일, 5일에 있는 상담을 하는 것이며, 이때의 이익은 10+20+15=45이다.

상담을 적절히 했을 때, 백준이가 얻을 수 있는 최대 수익을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N (1 ≤ N ≤ 1,500,000)이 주어진다.

둘째 줄부터 N개의 줄에 Ti와 Pi가 공백으로 구분되어서 주어지며, 1일부터 N일까지 순서대로 주어진다. (1 ≤ Ti ≤ 50, 1 ≤ Pi ≤ 1,000)

출력

첫째 줄에 백준이가 얻을 수 있는 최대 이익을 출력한다.

풀이 (동적 프로그래밍 실패)

시간 복잡도 O(n*m) 공간 복잡도 O(n) 

아이디어: n번째 요일날 그날 일해서 버는 돈을 포함하지 않고 전날까지 일한 금액의 최고 합계를 저장하는 dp[] 공간을 잡는다.

최종적으로는 n+1의 인덱스 값을 출력한다. 일해야 하는 시간을 days, 그에 받는 돈을 money 라고 할 때

dp[i+days] = Math.max(dp[i+days], dp[i]+money) 관계식이 세워진다. 

단 여기서 몇 가지 조건이 추가된다면 위에 작업은 특정 일자에만 할 수 있다는 점 과

dp[i]일날 벌 수 있는 최대의 금액이 i 보다 뒤에 존재하는 인덱스의 dp값 보다 크다면 dp[i] 값으로 최신화 할 필요가 있다는 점이다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        int n = Integer.parseInt(br.readLine());
        long[] dp = new long[n+2];
        int[][] info = new int[n+1][2];
        for (int i = 1; i <= n; i++) {
            st = new StringTokenizer(br.readLine());
            int days = Integer.parseInt(st.nextToken());
            int money = Integer.parseInt(st.nextToken());
            info[i] = new int[]{days,money};
        }
        for (int i = 1; i <= n; i++) {
            int days = info[i][0];
            int money = info[i][1];
            if(i+days<=n+1){
                for (int j = i+days; j <=n+1; j++) dp[j] = Math.max(dp[i+days],dp[i]+money);
            }

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

풀이 (동적 프로그래밍 성공)

시간 복잡도 O(n) 공간 복잡도 O(n)

아이디어: 위에 방식과 동일 하지만 불필요한 반복을 줄이기 위해 dp[i] = Math.max(dp[i],dp[i-1]);

어재의 금액이 오늘의 금액보다 크다면 최신화 하는 코드를 추가하고

체크되지 않는 마지막 dp[n+1]의 경우도 따로 비교 함으로써 시간 복잡도를 개선하여 통과하였다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        int n = Integer.parseInt(br.readLine());
        // n번째 요일날 그날 일해서 버는 돈을 포함하지 않고 그 전날까지 일한 금액의 최고 합계를 저장하는 dp
        long[] dp = new long[n+2];
        int[][] info = new int[n+1][2];
        for (int i = 1; i <= n; i++) {
            st = new StringTokenizer(br.readLine());
            int days = Integer.parseInt(st.nextToken());
            int money = Integer.parseInt(st.nextToken());
            info[i] = new int[]{days,money};
        }
        for (int i = 1; i <= n; i++) {
            // i+days 번째 요일 날 벌 수 있는 최고 금액의 합계가 그 전 값보다 낮은 경우 최신화
            dp[i] = Math.max(dp[i],dp[i-1]);
            int days = info[i][0];
            int money = info[i][1];
            if(i+days<=n+1) dp[i+days] = Math.max(dp[i+days],dp[i]+money);
        }
        long ans = Math.max(dp[n],dp[n+1]);
        System.out.println(ans);
    }
}

'PS > Gold' 카테고리의 다른 글

[Gold V] 암호코드 - 2011 (Java)  (0) 2023.07.27
[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25
[Gold V] 호텔 - 1106 (Java)  (0) 2023.07.23
[Gold III] 우주선 만들기 - 15912 (Java)  (0) 2023.07.22
[Gold V] 동전 2 - 2294  (0) 2023.07.20

문제 링크

성능 요약

메모리: 14272 KB, 시간: 128 ms

분류

다이나믹 프로그래밍, 배낭 문제

문제 설명

세계적인 호텔인 형택 호텔의 사장인 김형택은 이번에 수입을 조금 늘리기 위해서 홍보를 하려고 한다.

형택이가 홍보를 할 수 있는 도시가 주어지고, 각 도시별로 홍보하는데 드는 비용과, 그 때 몇 명의 호텔 고객이 늘어나는지에 대한 정보가 있다.

예를 들어, “어떤 도시에서 9원을 들여서 홍보하면 3명의 고객이 늘어난다.”와 같은 정보이다. 이때, 이러한 정보에 나타난 돈에 정수배 만큼을 투자할 수 있다. 즉, 9원을 들여서 3명의 고객, 18원을 들여서 6명의 고객, 27원을 들여서 9명의 고객을 늘어나게 할 수 있지만, 3원을 들여서 홍보해서 1명의 고객, 12원을 들여서 4명의 고객을 늘어나게 할 수는 없다.

각 도시에는 무한 명의 잠재적인 고객이 있다. 이때, 호텔의 고객을 적어도 C명 늘이기 위해 형택이가 투자해야 하는 돈의 최솟값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 C와 형택이가 홍보할 수 있는 도시의 개수 N이 주어진다. C는 1,000보다 작거나 같은 자연수이고, N은 20보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 각 도시에서 홍보할 때 대는 비용과 그 비용으로 얻을 수 있는 고객의 수가 주어진다. 이 값은 100보다 작거나 같은 자연수이다.

출력

첫째 줄에 문제의 정답을 출력한다.

풀이 (동적 프로그래밍 바텀 업)

시간 복잡도 O(n*m) 공간 복잡도 O(n)

아이디어: dp[] 공간을 n명을 채우기 위한 최소값의 저장 공간으로 생성, 비용과 비용대비 증가하는 사람의 수를 따로 배열로 저장

dp[n] = dp[n-people] + cost 의 관계식을 정의하여 풀이

"호텔의 고객을 적어도 C명 늘이기 위해" 즉 dp[C]를 출력하라는 것이 아니라 dp[C] 이상의 구간에서 그 보다 작은 값이 있으면 출력하여야 한다. 

그래서 필자는 c의 범위가 1000 아래라는 것을 이용해 일부로 dp공간을 1000더 잡아서 혹여나 dp[c] 다음 인덱스에 더 작은 값이 있을 경우 해당 값을 출력하도록 코드를 수정하였다.

import java.io.*;
import java.util.*;

public class Main {
    public static int sti(StringTokenizer st){
        return Integer.parseInt(st.nextToken());
    }
    public static long stl(StringTokenizer st){
        return Long.parseLong(st.nextToken());
    }
    public static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int c = Integer.parseInt(st.nextToken());
        int n = Integer.parseInt(st.nextToken());
        long[] dp = new long[c+1001];
        Arrays.fill(dp,Integer.MAX_VALUE);
        int[][] info = new int[n][2];
        dp[0] = 0;
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            int money = Integer.parseInt(st.nextToken());
            int people = Integer.parseInt(st.nextToken());
            info[i] = new int[]{money, people};
        }
        for (int i = 1; i <= c+1000; i++) {
            for (int j = 0; j < n; j++) {
                int money = info[j][0];
                int people = info[j][1];
                if(i-people>=0) dp[i] = Math.min(dp[i],dp[i-people]+money);
            }
        }

        long ans = dp[c];
        for (int i = c+1; i <=c+1000 ; i++) ans = Math.min(ans,dp[i]);

        System.out.println(ans);

    }
}

'PS > Gold' 카테고리의 다른 글

[Gold V] 전깃줄 - 2565 (Java)  (0) 2023.07.25
[Gold V] 퇴사 2 - 15486 (Java)  (0) 2023.07.24
[Gold III] 우주선 만들기 - 15912 (Java)  (0) 2023.07.22
[Gold V] 동전 2 - 2294  (0) 2023.07.20
[Gold II] 직각다각형 - 17611  (0) 2023.07.19

+ Recent posts