Java/코딩테스트

백준 2140, 28303, 2258

Bubbles 2023. 8. 22. 16:17

백준 2140 지뢰찾기  💣 

https://www.acmicpc.net/problem/2140

 

2140번: 지뢰찾기

지뢰찾기는 N×N에서 이뤄지는 게임이다. 보드의 곳곳에는 몇 개의 지뢰가 숨겨져 있고, 지뢰가 없는 칸에는 그 칸과 인접(상하좌우 및 대각선)해 있는 8개의 칸들에 몇 개의 지뢰가 숨겨져 있는

www.acmicpc.net

 

💣 테두리에 인접한 영역만 직접 계산하고, 나머지 가운데 영역들은 모두 지뢰가 있다고 가정해서 지뢰의 최대 개수를 계산한다. 

💣 그리디, 구현 문제로 분류되어 있다. 아래 그림의 분홍 형광펜 영역 기준 8방향을 탐색하는 Around함수, 그리고 Around 결과 따라 count를 감소시킬지 / 주변 숫자를 1씩 감소시킬지를 결정한다.

💣 나머진 아래 그림 참고 

💣 코드

import java.io.*;
public class p2140 {
    private static int[][] map;
    private static int[] dirX = { 0, -1, -1, -1, 0, 1, 1, 1};
    private static int[] dirY = { -1, -1, 0, 1, 1, 1, 0, -1};
    private static int n;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        if (n <= 2) {
            System.out.println(0);
            return;
        }
        map = new int[n][n];
        int count = (n-2)*(n-2);


        for(int i = 0; i < n; i++) {
            String temp = br.readLine();
            String[] split = temp.split("");
            for (int j = 0; j < n; j++) {
                if (split[j].equals("#")) {
                    map[i][j] = Integer.MIN_VALUE;
                } else {
                    map[i][j] = Integer.parseInt(split[j]);
                }
            }
        }


        for (int i = 1; i < n-1; i++) {
            for (int j = 1; j < n-1; j++) {
                if (i == 1 || i == n-2 || j == 1 || j == n-2) {
                    if (Around(i, j)) {
                        Delete(i, j);
                    } else { // 주변에 0이 있다는 뜻이니 무조건 그 칸은 지뢰후보 제외
                        count--;
                    }
                }
            }
        }
        System.out.println(count);

    }

    private static void Delete(int x, int y) {
        for (int i = 0; i < 8; i++) {
            int nextX = x + dirX[i];
            int nextY = y + dirY[i];

            if (!isValid(nextX, nextY)) {
                continue;
            }

            if (map[nextX][nextY] >= 0) {
                map[nextX][nextY]--;
            }

        }
    }
    private static boolean Around(int x, int y) {

        for (int i = 0; i < 8; i++) {
            int nextX = x + dirX[i];
            int nextY = y + dirY[i];

            if (!isValid(nextX, nextY)) {
                continue;
            }

            if (map[nextX][nextY] == 0) {
                return false;
            }

        }
        return true;
    }

    private static boolean isValid (int x, int y) {
        if (x < 0 || x > n-1 || y < 0 || y > n-1) {
            return false;
        }
        return true;
    }
}

 

 


 

백준 28303 자석 🧲

 

https://www.acmicpc.net/problem/28303

 

28303번: 자석

예제 1의 경우 N극이 3번 칸에 놓이고 S극이 5번 칸에 놓이도록 자석을 설치할 때 1번 현상으로 $a_3=22$의 에너지가 충전되며, 2번 현상으로 $a_5=4$의 에너지가 소모되고, 3번 현상으로 $(5-3)\times 2=4$

www.acmicpc.net

🧲 누적합 문제이다.... 무지성으로 풀다가 메모리 초과와 시간 초과를 번갈아 만날 수 있는 문제... 거의 안 풀어본 문제 유형이었는데 출처를 보니 알고리즘 대회문제였다. 동적 계획법으로 시작해서 누적합으로 끝난... 

🧲 문제에서 주어진 공식을 일반화 해서 식을 세운다. 

En - Es - (N index - S index) * K
= En - Es - (N index * K - S index * K)
= En - Es - N index * K + S index * K
= (En - N index * K) - (Es - S index * K)

 

🧲 에너지를 최대로 만들 수 있는 값을 계산하면 된다. (여기서 착각한게, 절대값의 최대를 계산하는게 아니다. -건 +건 다양한 변화량들 중 가장 큰 값을 찾는 문제이다. ) 이 에너지에는 아래와 같은 요소가 영향을 미친다. 

  • N극 에너지는 최대한 크게
  • S극 에너지는 최대한 작게
  • N극과 S극의 인덱스 차이 작게 (자석 길이 짧게)

따라서, 위 공식에서 En - N index * K 는 최대한 크게, Es - S index * K는 최대한 작게 만들어야 한다. 

Es - S index * K = left 로 정의하고 문제를 풀었다. 

 

🧲 풀이과정 첨부 

 

🧲 for문 2중으로 돌리면 시간초과가 난다. 그러므로 for 문 하나에 해결해나가는게 키 포인트

  • index 가 1부터 시작하는데, 얘는 N극과 S극의 인덱스 차이와 같다.
  • index를 이동하면서 에너지 총량을 Max로 갱신하는 작업 +  left는 위에서 설명했듯 최소로 만들어줘야 하므로 left를 Min으로 갱신하는 작업이 필요하다. 
  • 그리고 N-S 뒤집을 수도 있다. 그냥 편하게 reverse 배열에 에너지 반대로 넣은 다음 동일 로직으로 계산했다. 이정도는 메모리 제한에 걸리지 않았다.

 

🧲 전체 코드 

import java.io.*;
import java.util.*;
public class p28303 {
    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 answer = Long.MIN_VALUE;
        long[] num = new long[n];
        long[] reverse = new long[n];

        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            num[i] = Long.parseLong(st.nextToken());
            reverse[n-i-1] = num[i];
        }


        long left = num[0];
        for (int index = 1; index < n; index++) {
            // left가 S극을 의미, 가장 작아야만 에너지가 제일 크게 나옴
            answer = Math.max(answer, num[index] - ((long) index * k) - left);
            left = Math.min(left, num[index] - ((long) index * k));
        }

        left = reverse[0];
        for (int index = 1; index < n; index++) {
            answer = Math.max(answer, reverse[index] - ((long) index * k) - left);
            left = Math.min(left, reverse[index] - ((long) index * k));
        }

        System.out.println(answer);
    }
}

 

 

 


백준 2258 정육점 🥩

 

https://www.acmicpc.net/problem/2258

 

2258번: 정육점

첫째 줄에 두 정수 N(1 ≤ N ≤ 100,000), M(1 ≤ M ≤ 2,147,483,647)이 주어진다. N은 덩어리의 개수를 의미하고, M은 은혜가 필요한 고기의 양이다. 다음 N개의 줄에는 각 고기 덩어리의 무게와 가격을 나

www.acmicpc.net

 

🥩 Greedy, "가격은 적게, 양은 많게"가 이 문제가 요구하는 욕심(?)이다. 이를 기준으로 🥩를 우선순위큐에 넣기

🥩 주의사항 : 이 문제에서 가격은 같은데 양이 다른 고기가 있을 수도 있다.

  • 예를 들어 가격이 2인 고기가 양이 2, 3, 4인 채로 포장되어있다 하자.
  • 가격이 2이고 양이 4로 가장 많은 고기를 산다.
  • 그치만 양이 2, 3인 애들은 가격이 더 적은게 아니기 때문에 담을 수 없다. 
  • 2,3인 애들을 가져가려면 각각 가격만큼 추가로 지불해야한다. 이 조건을 빼먹지 말자. 

🥩 각 경우마다 총 지불할 금액, 고기총량, 고기들 중 최대 가격을 계산해주면 된다. 

🥩 고기 총량이 필요한 양을 넘긴 시점부터 Math.min으로 갱신해주면 된다. 

 

🥩 추가 설명 : 여기 있는 예제는 문제 예제 말고 일부러 같은 가격에 양 다른 고기를 넣어봤다. 

 

🥩 전체 코드

import java.io.*;
import java.util.*;
public class p2258 {
    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 need = Integer.parseInt(st.nextToken());
        int money = 0; // 사용 금액
        int maxMoney = 0;
        int amount = 0;
        int answer = Integer.MAX_VALUE;
        boolean isSuccess = false;

        PriorityQueue<Meat> pq = new PriorityQueue<>();
        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            int size = Integer.parseInt(st.nextToken());
            int price = Integer.parseInt(st.nextToken());
            pq.add(new Meat(size, price));
        }

        while (!pq.isEmpty()) {
            Meat poll = pq.poll();
            if (maxMoney < poll.price) {
                maxMoney = poll.price;
                money = poll.price;
            } else if (maxMoney == poll.price) {
                money += poll.price;
            }
            amount += poll.size;
            if (amount >= need) {
                answer = Math.min(answer, money);
                isSuccess = true;
            }
        }

        if (isSuccess) {
            System.out.println(answer);
        } else {
            System.out.println(-1);
        }

    }

    private static class Meat implements Comparable<Meat> {
        int size;
        int price;

        Meat(int size, int price) {
            this.size = size;
            this.price = price;
        }

        @Override
        public int compareTo(Meat o) {
            if (this.price == o.price) {
                return o.size - this.size;
            }
            return this.price - o.price;
        }
    }
}