백준 알고리즘 스터디 과제 정리 (SWaD) - 1

황제연·2024년 5월 14일
0

알고리즘

목록 보기
40/169
post-thumbnail
문제번호제목난이도
8980번택배골드 1
10775번공항골드 2
2293번동전 1골드 5
16236번아기 상어골드 3

8980번 택배

해결 코드:


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

class Lists{

    int start;
    int end;
    int count;

    public Lists(int start, int end, int count){
        this.start = start;
        this.end = end;
        this.count = count;
    }

}


public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int c = Integer.parseInt(st.nextToken());

        int m = Integer.parseInt(br.readLine());
        Lists[] list = new Lists[m];

        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            int count = Integer.parseInt(st.nextToken());

            list[i] = new Lists(start, end, count);
        }
        Arrays.sort(list, (o1, o2) -> {
            if(o1.end == o2.end){
                return o1.start - o2.start;
            }
            return o1.end - o2.end;
        });

        int[] arr = new int[n+1];
        for (int i = 1; i < n; i++) {
            arr[i] =  c;
        }



        int ans = 0;

        for (int i = 0; i < m; i++) {
            Lists li = list[i];

            int max = Integer.MAX_VALUE;

            for (int j = li.start; j < li.end; j++) {
                max = Math.min(max, arr[j]);
            }

            if(max >= li.count){
                for (int j = li.start; j < li.end; j++) {
                    arr[j] -= li.count;
                }
                ans += li.count;
            }else{
                for (int j = li.start; j < li.end; j++) {
                    arr[j] -= max;
                }
                ans += max;
            }
        }
        bw.write(ans +"");


        br.close();
        bw.close();
    }
}

해결 키포인트:

  • 배송 정보 리스트 정렬을 다음과 같이 해야한다:
    -> 1번 우선순위: 받는 마을, 2번 우선순위: 보내는 마을
  • 각 마을 별로, 최대로 보낼 수 있는 박스의 개수를 설정해둔다.
    -> 초기 용량은 트럭의 용량 c로 지정한다
  • 배송 정보 리스트를 확인하면서 보내는 구간부터 받는 구간까지 구간 별로
    최대로 보낼 수 있는 용량을 계산해서 각 마을별로 보낼 수 있는 박스의 개수를 갱신한다.

고민/풀이흐름:

  1. 일단 문제를 읽으면서 상태 정보들을 잘 관리해야겠다는 생각을 하게되었다
  2. 배송정보는 클래스로 만들어서 클래스타입의 리스트로 관리하고, 각 마을별로 배열을 만들어 보낼 수 있는 박스의 개수 상태도 관리한다
  3. 최대 박스 개수를 보내기 위해 조금 그리디하게 생각해볼 필요가 있다. 따라서 처음에는 보내는 곳, 받는 곳 기준으로 정렬을 진행하였다
  4. 하지만 반례가 발생하였고, 이번에는 받는 곳 -> 보내는 곳 기준으로 오름차순 정렬을 진행하였다.
  5. 이후 풀이는 조금 떠올리는데 고민을 많이하였다. 정렬대로만 풀자니 순서 관리가 쉽지가 않고, 다른 방법을 모색하자니 그것도 쉽게 떠오르지 않았다
  6. 일단 각 마을별로 보낼 수 있는 박스의 개수 상태를 관리하도록 하였다. 이때 초기값은 트럭이 실을 수 있는 최대 용량으로 한다
  7. 이제 배송 정보를 보면서 각 배송정보의 라인 별로 상태를 관리하면서 값을 갱신해준다.
  8. 먼저 max에 각 라인에서 보낼 수 있는 최대치를 구해준다.
  9. 최대치가 만약 현재 배송 정보에서 보내려고 하는 크기보다 크거나 같으면 각 라인에서 그 보내려는 크기를 빼주고 ans에 그 값을 더해준다.
  10. 최대치가 더 크면 최대치로 배송하는 것이므로 똑같이 라인에서 max를 빼주고 ans에 max를 더해준다
  11. 해당방식으로 진행하여 ans를 출력하면 정답이 된다.

링크:

8980번 - 택배

10775번 공항

해결 코드:


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


public class Main {

    static int[] parent;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int g = Integer.parseInt(br.readLine());
        int p = Integer.parseInt(br.readLine());
        int[] arr = new int[p];
        for (int i = 0; i < p; i++) {
            arr[i] = Integer.parseInt(br.readLine());
        }

        parent = new int[g+1];
        for (int i = 1; i < g+1; i++) {
            parent[i] = i;
        }

        int ans = 0;
        for (int i = 0; i < p; i++) {
            int prepare = find(arr[i]);
            if(prepare == 0){
                break;
            }

            ans++;
            union(prepare, prepare-1);
        }

        bw.write(ans+"");
        
        br.close();
        bw.close();
    }

    private static int find(int x){
        if(x == parent[x]){
            return x;
        }
        return parent[x] = find(parent[x]);
    }

    private static void union(int x, int y){
        x = find(x);
        y = find(y);

        if(x!= y){
            parent[x] = y;
        }
    }

}

해결 키포인트:

  • 문제를 이해하기만 하면 쉽게 풀 수 있다.
  • 비행기가 도킹하려고 할 때, 최적의 게이트는 그 비행기가 도킹하고자 하는 게이트이다.
  • 만약 해당 게이트에 비행기가 있다면 그 이전 게이트로 도킹하도록 하면 된다.
  • 위와 같이 그리디하게 풀면 해결할 수 있으며, 추가로 유니온파인드 까지 이용하면 매우 쉽게 풀 수 있다.

고민/풀이흐름:

  1. 비행기가 들어오려는 게이트 값들은 배열에 받아둔다
  2. 유니온 파인드 설정을 마치고, 배열에 있는 값들을 불러와서 그 부모 값을 찾아준다.
  3. 이때 처음 들어올때는 자기 자신의 게이트에 도킹하게 된다. 만약 자기 자신의 게이트에 이미 도킹이 되어있다면 바로 이전 게이트에 도킹한다. 이때 union을 해준다
  4. 만약 현재 도킹하려는 게이트가 0이라면 더이상 도킹할 수 없으므로 종료해준다
  5. 각 순회마다 ans값을 증가시켜서 정답으로 출력해준다.

링크:

10775번 - 공항

2293번 동전 1

해결 코드:


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));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int k = Integer.parseInt(st.nextToken());


        int[] dp = new int[k+1];
        dp[0] = 1;

        for (int i = 1; i < n+1; i++) {
            int p1 = Integer.parseInt(br.readLine());
            for (int j = p1; j < k+1; j++) {
                dp[j] += dp[j - p1];
            }
        }

        bw.write(dp[k]+"");

        br.close();
        bw.close();
    }
}

해결 키포인트:

  • 너무 어려운 dp...
  • 점화식은 힌트 참고했습니다;
  • 해당 문제에서 dp 배열은 자릿수를 만들 수 있는 숫자를 의미하도록 정의하였다
  • 입력값인 p1으로 각 자릿수를 만들 수 있는 경우를 더하는 방식으로 풀면 된다.

고민/풀이흐름:

  1. dp[0]은 1로 지정하고 p1부터 k까지 순회하면서 dp[j]에 dp[j-p1]을 더해준다
  2. p1부터 k까지 1번을 반복한 뒤, dp[k]를 출력한다.

링크:

2293번 - 동전 1

16236번 아기 상어

해결 코드:


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


class Pos{

    int y;
    int x;
    int count;

    public Pos(int y, int x, int count) {
        this.y = y;
        this.x = x;
        this.count = count;
    }
}

public class Main {

    static int count = 0;
    static boolean[][] visited;
    static int[] dy = {-1,0,0,1};
    static int[] dx = {0,-1,1,0};
    static int[][] arr;
    static int size = 2;
    static int eat = 0;
    static boolean chk;
    static Pos start;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int n = Integer.parseInt(br.readLine());
        arr = new int[n][n];


        for (int i = 0; i < n; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
                if(arr[i][j] == 9){
                  start = new Pos(i,j, 0);
                  arr[i][j] = 0;
                }
            }
        }
        while(true){

            chk = false;
            bfs(n,start);

            if(!chk){
                break;
            }

            if(size == eat){
                size++;
                eat = 0;
            }
        }


        bw.write(count+"");

        br.close();
        bw.close();
    }

    private static void bfs(int n, Pos s) {
        PriorityQueue<Pos> pq = new PriorityQueue<>((o1, o2) -> {
            if(o1.count == o2.count){
                if(o1.y == o2.y){
                    return o1.x - o2.x;
                }
                return o1.y - o2.y;
            }
            return o1.count - o2.count;
        });

        pq.add(s);
        visited = new boolean[n][n];
        visited[s.y][s.x] = true;

        while(!pq.isEmpty()){
            Pos now = pq.poll();

            if(arr[now.y][now.x] != 0 && arr[now.y][now.x] < size){
                arr[now.y][now.x] = 0;
                eat++;
                count += now.count;
                start.count = 0;
                start.y = now.y;
                start.x = now.x;
                chk = true;
                return;
            }


            for (int i = 0; i < 4; i++) {
                int ny = now.y + dy[i];
                int nx = now.x + dx[i];
                if(ny >= 0 && nx >= 0 && ny < n && nx < n && !visited[ny][nx]){
                    if(arr[ny][nx] <= size){
                        pq.add(new Pos(ny, nx, now.count+1));
                        visited[ny][nx] = true;
                    }
                }
            }
        }

    }
}

해결 키포인트:

  • BFS와 약간의 구현을 더해주는 방식.
  • 하지만 이번 BFS에서는 우선순위 큐를 사용해야 한다.
  • 탐색이 종료될 때마다, 시작 위치를 갱신해야하고 주어진 조건에 맞게 상어의 크기보다 작다면 해당 위치를 지워주어야 한다

고민/풀이흐름:

  1. 문제에서 주어진 조건에 맞춰서 BFS와 구현을 같이 진행하면 되는 문제로 생각하였다
  2. 먼저 배열에 입력값을 받고 초기값의 위치를 미리 받아둔다.
  3. 초기값의 위치는 배열로도 받을 수 있지만 문제의 편의를 위해 클래스로 미리 지정해두었다
  4. 세번째 필드인 count는 지금까지 탐색한 횟수이다
  5. 먼저 아기상어가 탐색을 더이상 못한다는 플래그를 하나 설정해두었다. chk 불리언을 통해 판단할 것이다
  6. bfs탐색을 진행한다. 이때 원래의 BFS와 다른점이 일반 큐가 아니라 우선순위 큐를 사용한다는 점이다.
  7. 왜냐하면 주어진 조건에 맞추기 위해 큐에서 정렬이 지속적으로 이루어져야하기 때문이다
  8. 가장 가까운 위치, 위쪽, 왼쪽 순으로 상어가 먹이를 먹어야하므로 각각 조건에 맞춰서 오름차순 정렬을 하도록 설정하였다
  9. 이어서는 기존처럼 bfs 탐색을 진행하면 된다
  10. 탐색을 할 때는, 상어와 크기가 같더라도 탐색 범위에 넣어준다
  11. 하지만 현재의 값이 0이 아니고, 상어의 크기보다 작다면 해당 위치를 0으로 초기화, 먹은 숫자 증가, 현재까지 이동한 위치의 수를 더해준다
  12. 그리고 시작 위치 갱신을 진행하며, chk를 true로 해서 다시 반복하도록 한다
  13. bfs 종료 후에는 현재 상어의 크기와 먹은 양이 같은지 확인해준다
  14. 만약 같다면 먹은 양은 0으로 초기화하고 size의 값을 증가시킨다
  15. 위 과정을 chk가 false가 아닐때까지 반복을 해주고 종료 후에는 count를 출력해주면 정답이 된다.

링크:

16236번 - 아기 상어

profile
Software Developer

0개의 댓글