벨만 포드를 오랜만에 봤더니 기억이 잘 나질않아서 어려웠다.

 

벨만 포드 알고리즘이란 ?

한 지점에서 다른 모든 지점까지 최단 경로를 찾는 알고리즘은 다익스트라와 벨만 포드가 있다.

여기서 두 알고리즘의 차이는 무엇이냐 ?!!

바로 속도 차이다. 다익스트라가 벨만 포드보다 빠르다. 이유는 다익스트라의 동작 방식은 방문하지 않은 정점 중에서 가장 비용이 적은 노드를 선택한다. 해당 정점을 거쳐서 다른 정점을 가는 경우를 고려하여 최비용을 갱신한다. 이 부분을 반복한다.

이런식으로 하면 편하게 생각해서 정점 갯수 만큼 동작하면 갱신된다 ( (정점,도착비용)을 우선순위 큐에 넣고 최단인 경우 정점을 방문하고 그 외는 다른 동작을 하지않음 )

 

그치만 다익스트라는 음수가 포함되어있으면 제대로 동작되지 않는다. 그렇기때문에 다익스트라를 사용할 수 없는 경우에 벨만 포드 알고리즘이 사용된다.

벨만 포드는 간단하다. 

(정점의 갯수 - 1)번 동안 모든 간선을 확인하면서 최소값으로 갱신하면 최단거리가 나온다.

(정점의 갯수 - 1)번은 가장 긴 최단거리는 1 에서 n을 간다고 할때 1 - 2 -3 -...-n 의 결과이기때문에 n까지 V - 1번 갱신하면된다. ( 물론 그 이전에 구해질 수도 있다. )

여기서 중요한 점은 최단거리를 구하고나서 한번 더! 모든 간선을 확인하면서 최소값을 갱신했을때 갱신이 된다면 음의 사이클이 존재하는 것이다. 그렇기 때문에 최단거리를 구할 수없다 ( 값이 계속 작아져요 )

 

자 이제 이론은 다 배웠다.

 

접근방법

  1. 벨만 포드를 사용한다.

입 / 출력

입력 3 2
2 3 -1
3 2 -2
               
출력 -1
-1
               

 

코드

더보기
import java.io.*;
import java.util.*;

public class Main {

    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer token;

    static int readInt() throws IOException {
        if (Objects.isNull(token) || !token.hasMoreTokens()) {
            token = new StringTokenizer(reader.readLine());
        }
        return Integer.parseInt(token.nextToken());
    }

    static class Node {
        int v, cost;

        public Node(int v, int cost) {
            this.v = v;
            this.cost = cost;
        }
    }

    public static void main(String[] arg) throws IOException {
        int vertexSize = readInt();
        int edgeSize = readInt();
        List<List<Node>> bus = new ArrayList<>();
        for (int i = 0; i <= vertexSize; i++) {
            bus.add(new ArrayList<>());
        }
        for (int i = 0; i < edgeSize; i++) {
            int u = readInt();
            int v = readInt();
            int cost = readInt();

            bus.get(u).add(new Node(v, cost));
        }

        long[] distances = new long[vertexSize + 1];

        Arrays.fill(distances, Integer.MAX_VALUE);
        distances[1] = 0;

        for (int i = 0; i < vertexSize - 1; i++) {
            for (int j = 1; j <= vertexSize; j++) {
                for (Node next : bus.get(j)) {
                    if (distances[j] != Integer.MAX_VALUE && distances[j] + next.cost < distances[next.v]) {
                        distances[next.v] = distances[j] + next.cost;
                    }
                }
            }
        }

        long compareDistance[] = distances.clone();
        for (int i = 1; i <= vertexSize; i++) {
            for (Node next : bus.get(i)) {
                if (distances[i] != Integer.MAX_VALUE && distances[i] + next.cost < distances[next.v]) {
                    distances[next.v] = distances[i] + next.cost;
                }
            }
        }
        for (int i = 1; i < vertexSize; i++) {
            if (compareDistance[i] != distances[i]) {
                System.out.println("-1");
                return;
            }
        }
        for (int i = 2; i <= vertexSize; i++) {
            System.out.println(distances[i] == Integer.MAX_VALUE ? -1 : distances[i]);
        }

    }
}

1. 벨만 포드 알고리즘 탐색을 할때 최단거리를 갱신할때 현재 위치 비용이 최댓값 ( INF ) 일 경우에 코드가 동작하지 않게 설정해야된다. 음수의 간선을 가지고 있는 경우에 갱신되는 경우가 생기기때문이다.

2. 언더플로우가 발생할 수 있다. 자료형을 long으로 설정해야된다. 이유는 정점이 500개 이고 노선의 갯수가 6000개 일때 모든 간선이 -10000을 가지고 있다면 간선이 계속 줄어든다.  500*6000 번 감소된다. (1번 할때마다 -10000씩 갱신된다)

 

 

 

 

 

 

'개인 공부 > 백준' 카테고리의 다른 글

2470번 두 용액 ( JAVA )  (2) 2024.09.22
1300번 K번째 수 ( JAVA )  (0) 2024.09.19
11652번 카드 ( JAVA )  (1) 2024.09.09
11401번 이항 계수 3  (1) 2024.09.08
2156번 포도주 시식 ( JAVA )  (1) 2024.09.04

접근방법

  1. 정렬을 한다.
  2. 제일 작은 값과 제일 큰값을 기준으로 더해서 절대값을 비교한다.
  3. 만약 작은 값과 큰 값을 더했을때 0 보다 작으면 left 값을 증가
  4. 만약 작은 값과 큰 값을 더했을때 0보다 크거나 작으면 right 값을 감소

입 / 출력

입력 5
-2 4 -99 -1 98
5
-6 -8 -1 -2 -3
10
-87 -42 -40 -22 -11 23 29 78 79 98
3
-10 1 2
5
6 8 1 2 3
4
999999995 999999996 999999997 1000000000
출력 -99 98 -2 -1 -22 23 1 2 1 2 999999995 999999996

 

코드

더보기
import java.util.*;
import java.io.*;

public class 두_용액 {

    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer token;

    static long readLong() throws IOException {
        if (Objects.isNull(token) || !token.hasMoreTokens()) {
            token = new StringTokenizer(reader.readLine());
        }
        return Long.parseLong(token.nextToken());
    }

    static int readInt() throws IOException {
        if (Objects.isNull(token) || !token.hasMoreTokens()) {
            token = new StringTokenizer(reader.readLine());
        }
        return Integer.parseInt(token.nextToken());
    }

    public static void main(String[] arg) throws IOException {
        int n = readInt();
        ArrayList<Long> list = new ArrayList<>();

        for (long i = 0; i < n; i++) {
            list.add(readLong());
        }

        Collections.sort(list);

        int left = 0;
        int right = n - 1;

        long minValue = Long.MAX_VALUE;
        long result[] = new long[2];
        while (left < right) {
            long value = list.get(left) + list.get(right);

            if (Math.abs(value) < minValue) {
                minValue = Math.abs(value);
                result[0] = list.get(left);
                result[1] = list.get(right);
            }

            if (value < 0)
                left++;
            else
                right--;

        }

        System.out.println(result[0] + " " + result[1]);
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

11657번 타임머신 ( JAVA )  (0) 2024.09.24
1300번 K번째 수 ( JAVA )  (0) 2024.09.19
11652번 카드 ( JAVA )  (1) 2024.09.09
11401번 이항 계수 3  (1) 2024.09.08
2156번 포도주 시식 ( JAVA )  (1) 2024.09.04

접근방법

  1. 1 부터 n*n 사이에 정답이 존재함
  2. x보다 작은 값을 찾아야한다. 각 칸은 (행 * 열)로 계산되고 각 i행은 (i * 열) 값으로 계산된다. 여기서 이 값이 1부터 n*n 까지 탐색을 시작한다
  3. 중간 값으로 탐색을 시작하면 중간 값보다 작은 값들의 수를 찾으면된다. (중간 값보다 작거나 같은 갯수가 k개 이상 이면 k번째 수는 중간값, 이상인 이유는 중복된 값이 존재하기 때문에 결과가 k개 이상이 출현할수 있음, 이중에 가장 최소한의 k를 찾으면 됨)
  4. 중간 값보다 작은 값들의 수는 각 행마다 ( 행번호 * 열번호 ) <= 중간값 이어야되는 조건에서 열번호 <= 중간값 / 행번호 가된다. 이 열번호는 1번 부터 시작하기때문에 갯수가 된다.
  5. 여기서 열번호는 최대 n개 이기때문에 min(n , 중간값/행번호) 이다.

스케치

입 / 출력

입력 3
7
2
1
2
2
2
3
2
4
10
100
1
1
출력 6 1 2 2 4 100 1

 

코드

더보기
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.Objects;

public class K번째_수 {

    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer token;

    public static long readLong() throws IOException {
        if (Objects.isNull(token) || !token.hasMoreTokens()) {
            token = new StringTokenizer(reader.readLine());
        }
        return Long.parseLong(token.nextToken());
    }

    static long N;
    static long K;

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

        N = readLong();
        K = readLong();

        System.out.println(binarySearch(1, N * N));

    }

    public static long binarySearch(long start, long end) {

        long left = start;
        long right = end;

        while (left <= right) {

            long mid = left + (right - left) / 2;
            long count = 0;

            for (int i = 1; i <= N; i++) {
                count += Math.min(N, mid / i);
            }

            if (count < K) {
                left = mid + 1;
            } else {

                right = mid - 1;
            }
        }
        return right + 1;

    }
}

 

코드를 작성하고 맞왜틀 상황이였다. 처음에 값을 int 형으로 받고 함수로 전달할때 n*n을 해서 보냈더니 오버플로우가 발생해서 틀린거였다. long 타입으로 변경해서 해결했다.

'개인 공부 > 백준' 카테고리의 다른 글

11657번 타임머신 ( JAVA )  (0) 2024.09.24
2470번 두 용액 ( JAVA )  (2) 2024.09.22
11652번 카드 ( JAVA )  (1) 2024.09.09
11401번 이항 계수 3  (1) 2024.09.08
2156번 포도주 시식 ( JAVA )  (1) 2024.09.04

접근방법

  1. 숫자마다 갯수를 샌다
  2. 여기서는 매우 큰수 이기때문에 배열과 같은 것 보다는 숫자와 횟수를 저장하는 Map 구조를 사용했다.

입 / 출력

입력 4
1
1
2
2
4
2
2
1
1
1
1
6
1
2
1
2
1
2
5
1
2
1
2
1
1
1000000000
출력 1 1 1 1 1 1000000000

 

코드

더보기

내가 작성한 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class 카드 {
    public static void main(String[] arg) throws NumberFormatException, IOException {
        HashMap<Long, Integer> map = new HashMap<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());

        for (int i = 0; i < n; i++) {
            Long m = Long.parseLong(reader.readLine());

            if (map.get(m) != null) {
                map.put(m, map.get(m) + 1);
            } else
                map.put(m, 1);
        }
        List<Map.Entry<Long, Integer>> list = new ArrayList<>(map.entrySet());

        Long value = Long.MIN_VALUE;
        int count = 0;
        for (Map.Entry<Long, Integer> item : list) {
            if (item.getValue() == count) {
                value = Math.min(value, item.getKey());
            } else if (item.getValue() > count) {
                value = item.getKey();
                count = item.getValue();
            }

        }
        System.out.println(value);
    }
}

 

클린코드로 변환 요청한 공부용 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.OptionalLong;

public class CardFrequency {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        Map<Long, Integer> cardFrequencyMap = new HashMap<>();

        // 카드 번호를 입력받고 빈도를 계산하는 부분
        for (int i = 0; i < n; i++) {
            Long cardNumber = Long.parseLong(reader.readLine());
            cardFrequencyMap.put(cardNumber, cardFrequencyMap.getOrDefault(cardNumber, 0) + 1);
        }

        // 가장 빈도가 높은 카드와, 빈도가 같을 경우 가장 작은 값을 찾는 부분
        OptionalLong mostFrequentCard = cardFrequencyMap.entrySet()
                .stream()
                .max((entry1, entry2) -> {
                    int freqCompare = entry1.getValue().compareTo(entry2.getValue());
                    if (freqCompare == 0) {
                        return entry1.getKey().compareTo(entry2.getKey());
                    }
                    return freqCompare;
                })
                .map(Map.Entry::getKey)
                .stream()
                .mapToLong(Long::longValue)
                .findFirst();

        mostFrequentCard.ifPresent(System.out::println);
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

2470번 두 용액 ( JAVA )  (2) 2024.09.22
1300번 K번째 수 ( JAVA )  (0) 2024.09.19
11401번 이항 계수 3  (1) 2024.09.08
2156번 포도주 시식 ( JAVA )  (1) 2024.09.04
2580번 스도쿠 ( JAVA )  (1) 2024.09.03

접근방법

  1. 이항계수 식을 보면 n! / ( r! * (n-r)! ) 이다.
  2. N!까지의 팩토리얼 값을 저장한다. ( 1,000,000,007 나눈 나머지 저장 ) 
  3. 모듈러 연산에서 나눗셈을 할 수 없기때문에 역원을 구해야된다. ( 역원 = 역수와 비슷하다 설명어려워ㅜ)
  4. n! * r!역원 * (n-r)!역원 으로 계산할것이다.
  5. 역원을 구하는 과정에서 페르마의 소정리를 사용한다.
  6. 수포자는 공부해야된다.

입 / 출력

입력 5 2
출력 10

 

코드

더보기

이 코드는 GPT한테 클린코드로 만들어 달라고했당 가독성이 너무 떨어져서 공부하기엔 안좋았다.

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

public class Main {

    static final int MOD = 1_000_000_007; // 모듈러 값
    static long[] factorial;              // 팩토리얼 배열
    static long[] inverseFactorial;       // 팩토리얼 역원 배열

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] input = reader.readLine().split(" ");
        int n = Integer.parseInt(input[0]);
        int k = Integer.parseInt(input[1]);

        precomputeFactorials(n);

        long binomialCoefficient = computeBinomialCoefficient(n, k);
        System.out.println(binomialCoefficient);
    }

    // 팩토리얼과 역팩토리얼을 미리 계산하여 저장
    private static void precomputeFactorials(int n) {
        factorial = new long[n + 1];
        inverseFactorial = new long[n + 1];

        factorial[0] = 1; // 0! = 1

        // 팩토리얼 값 계산
        for (int i = 1; i <= n; i++) {
            factorial[i] = factorial[i - 1] * i % MOD;
        }

        // 마지막 팩토리얼 값에 대한 모듈러 역원 구하기
        inverseFactorial[n] = modInverse(factorial[n], MOD);

        // 나머지 역팩토리얼 값 구하기
        for (int i = n - 1; i >= 0; i--) {
            inverseFactorial[i] = inverseFactorial[i + 1] * (i + 1) % MOD;
        }
    }

    // 이항계수 계산
    private static long computeBinomialCoefficient(int n, int k) {
        if (k < 0 || k > n) {
            return 0; // nCk가 정의되지 않는 경우
        }
        return factorial[n] * inverseFactorial[k] % MOD * inverseFactorial[n - k] % MOD;
    }

    // 모듈러 역원 구하기 (페르마의 소정리 사용)
    private static long modInverse(long a, int mod) {
        return pow(a, mod - 2, mod); // a^(mod-2) % mod
    }

    // 빠른 거듭제곱 계산 (a^b % mod)
    private static long pow(long base, long exp, long mod) {
        long result = 1;
        while (exp > 0) {
            if (exp % 2 == 1) {
                result = result * base % mod; // 지수가 홀수인 경우
            }
            base = base * base % mod; // 제곱
            exp /= 2; // 지수 절반으로 줄이기
        }
        return result;
    }

}

'개인 공부 > 백준' 카테고리의 다른 글

1300번 K번째 수 ( JAVA )  (0) 2024.09.19
11652번 카드 ( JAVA )  (1) 2024.09.09
2156번 포도주 시식 ( JAVA )  (1) 2024.09.04
2580번 스도쿠 ( JAVA )  (1) 2024.09.03
14888번 연산자 끼워넣기 ( JAVA )  (1) 2024.09.02

접근방법

  1. DP는 어려웡
  2. 현재 포도주에 대한 3가지 행동을 할 수 있다.
    1. 이전에 쉬고 마시기 ( 그 이전까지 최대 값을 가져옴 )
    2. 이전에 마시기 ( 이건 이전에 먹으면 그 이전에 한번 쉬고 온것 까지 계산해야됨)
    3. 안 마시기 ( 이전의 최대값 그대로 가져옴 )

입 / 출력

입력 3
1
1
0
3
1
0
1
3
0
1
1
1
1
2
1
2
4
5
5
5
5
5
0
0
10
0
0
출력 2 2 2 1 3 15 10

 

코드

더보기
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 포도주_시식 {

    public static void main(String[] arg) throws IOException {
        // BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(reader.readLine());
        int amountOfWine[] = new int[n];

        for (int i = 0; i < n; i++) {
            amountOfWine[i] = Integer.parseInt(reader.readLine());
        }
        if (n == 1) {
            // 와인이 1잔일 때는 그 1잔만 마신다.
            System.out.println(amountOfWine[0]);
            return;
        } else if (n == 2) {
            // 와인이 2잔일 때는 두 잔 모두 마시는 것이 최대
            System.out.println(amountOfWine[0] + amountOfWine[1]);
            return;
        }

        int[] dp = new int[n];
        dp[0] = amountOfWine[0];
        dp[1] = amountOfWine[0] + amountOfWine[1];
        dp[2] = Math.max(dp[1], Math.max(amountOfWine[0] + amountOfWine[2], amountOfWine[1] + amountOfWine[2]));
        for (int i = 3; i < n; i++) {
            dp[i] = Math.max(dp[i - 3] + amountOfWine[i - 1] + amountOfWine[i], dp[i - 2] + amountOfWine[i]);
            dp[i] = Math.max(dp[i], dp[i - 1]);
        }
        System.out.println(dp[n - 1]);
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

11652번 카드 ( JAVA )  (1) 2024.09.09
11401번 이항 계수 3  (1) 2024.09.08
2580번 스도쿠 ( JAVA )  (1) 2024.09.03
14888번 연산자 끼워넣기 ( JAVA )  (1) 2024.09.02
2346번 풍선 터뜨리기 ( JAVA )  (0) 2024.09.01

접근방법

  1. 백트래킹을 사용하는데 모든 칸을 확인한다.
  2. 칸을 확인하면서 빈칸인 부분에 들어갈 수 있는 숫자를 구한다.
  3. 숫자를 칸에 넣고 백트래킹~ 후 다시 복귀

입 / 출력

입력 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 8 0 0 0 0 0 4 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
0 0 3 0 2 0 6 0 0
9 0 0 3 0 5 0 0 1
0 0 1 8 0 6 4 0 0
0 0 8 1 0 2 9 0 0
7 0 0 0 0 0 0 0 8
0 0 6 7 0 8 2 0 0
0 0 2 6 0 9 5 0 0
8 0 0 2 0 3 0 0 9
0 0 5 0 1 0 3 0 0
5 0 0 0 0 8 0 0 0
0 3 0 0 0 0 0 0 4
0 0 7 0 0 0 0 0 0
0 6 0 0 0 0 0 0 0
0 0 0 0 9 0 0 0 0
0 0 0 0 0 0 0 5 0
0 0 0 0 0 0 4 0 0
9 0 0 0 0 0 0 2 0
0 0 0 6 0 0 0 0 1
출력 1 2 3 4 5 6 7 8 9 
4 5 7 8 1 9 2 3 6
6 9 8 3 2 7 1 5 4
2 1 4 5 3 8 6 9 7
3 8 6 7 9 1 5 4 2
5 7 9 6 4 2 3 1 8
7 3 1 9 6 4 8 2 5
8 4 5 2 7 3 9 6 1
9 6 2 1 8 5 4 7 3
5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9
4 8 3 9 2 1 6 5 7 
9 6 7 3 4 5 8 2 1
2 5 1 8 7 6 4 9 3
5 4 8 1 3 2 9 7 6
7 2 9 5 6 4 1 3 8
1 3 6 7 9 8 2 4 5
3 7 2 6 8 9 5 1 4
8 1 4 2 5 3 7 6 9
6 9 5 4 1 7 3 8 2
5 1 2 3 4 8 6 7 9 
6 3 8 2 7 9 5 1 4
4 9 7 1 5 6 2 3 8
1 6 3 4 2 5 8 9 7
2 7 5 8 9 1 3 4 6
8 4 9 7 6 3 1 5 2
3 2 1 9 8 7 4 6 5
9 8 6 5 1 4 7 2 3
7 5 4 6 3 2 9 8 1

 

코드

더보기
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static int map[][];

    static boolean back() {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (map[i][j] == 0) {
                    boolean[] check = new boolean[9];
                    for (int k = 0; k < 9; k++) {
                        if (map[k][j] != 0)
                            check[map[k][j] - 1] = true;
                    }
                    for (int k = 0; k < 9; k++) {
                        if (map[i][k] != 0)
                            check[map[i][k] - 1] = true;
                    }
                    int nextY = i / 3 * 3;
                    int nextX = j / 3 * 3;
                    for (int k = 0; k < 3; k++) {
                        for (int l = 0; l < 3; l++) {
                            if (map[nextY + k][nextX + l] != 0) {
                                check[map[nextY + k][nextX + l] - 1] = true;
                            }
                        }
                    }
                    for (int k = 0; k < 9; k++) {
                        if (!check[k]) {
                            map[i][j] = k + 1;
                            if (back()) {
                                return true;
                            }
                            map[i][j] = 0;
                        }
                    }
                    return false;
                }
            }
        }
        return true;

    }

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

        // BufferedReader br = new BufferedReader(new FileReader("input.txt")); 파일입출력 테스트
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        map = new int[9][9];
        for (int i = 0; i < 9; i++) {
            String[] line = br.readLine().split(" ");
            for (int j = 0; j < 9; j++) {
                map[i][j] = Integer.parseInt(line[j]);
            }
        }
        if (back()) {
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    System.out.print(map[i][j] + " ");
                }
                System.out.println();
            }
        }
    }
}

접근방법

  1. 백트래킹을 통해 연산자를 하나씩 사용하면서 수식을 만든다.

입 / 출력

입력 2
1 1
1 0 0 0
11
100 100 100 100 100 100 100 100 100 100 100
10 0 0 0
3
100 50 25
0 0 0 2
출력 2
2
1100
1100
0
0
입력 5
10 20 30 40 50
1 1 1 1
11
1 1 1 1 1 1 1 1 1 1 1
10 0 0 0
11
1 1 1 1 1 1 1 1 1 1 1
0 10 0 0
출력 2000
-1950
11
11
-9
-9
입력 11
1 1 1 1 1 1 1 1 1 1 1
0 0 10 0
11
1 1 1 1 1 1 1 1 1 1 1
0 0 0 10
4
10 20 30 40
1 2 0 0
출력 1
1
1
1
0
-40

 

코드

더보기
더보기
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class 연산자_끼워넣기 {
    static int n;
    static int maxValue = Integer.MIN_VALUE;
    static int minValue = Integer.MAX_VALUE;
    static int op[];
    static int numbers[];

    public static void main(String[] args) throws IOException {
        // BufferedReader br = new BufferedReader(new FileReader("input.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        n = Integer.parseInt(br.readLine());
        numbers = new int[n];
        String[] intData = br.readLine().split(" ");

        for (int i = 0; i < n; i++) {
            numbers[i] = Integer.parseInt(intData[i]);
        }

        String[] opData = br.readLine().split(" ");
        op = new int[4];
        for (int i = 0; i < 4; i++) {
            op[i] = Integer.parseInt(opData[i]);
        }
        findOP(0, numbers[0]);
        System.out.println(maxValue);
        System.out.println(minValue);
    }

    static void findOP(int k, int result) {
        if (k == n - 1) {
            maxValue = Math.max(maxValue, result);
            minValue = Math.min(minValue, result);
            return;
        }

        if (op[0] > 0) {
            op[0]--;
            findOP(k + 1, result + numbers[k + 1]);
            op[0]++;
        }

        if (op[1] > 0) {
            op[1]--;
            findOP(k + 1, result - numbers[k + 1]);
            op[1]++;
        }

        if (op[2] > 0) {
            op[2]--;
            findOP(k + 1, result * numbers[k + 1]);
            op[2]++;
        }

        if (op[3] > 0) {
            op[3]--;
            findOP(k + 1, result / numbers[k + 1]);
            op[3]++;
        }

    }

}

이 문제는 덱을 사용해서 왼쪽 오른쪽 회전을 통해 풀었다.

 

접근방법

  1. 덱에 1부터 N까지 넣는다.
  2. 덱에서 값을 꺼낸 후 출력한다.
  3. 덱에서 꺼낸 값의 인덱스에 적혀있는 수를 읽어온다.
  4. 인덱스에 적혀있는 수만큼 왼쪽 오른쪽으로 이동시킨다 ( 뒤에서 빼서 앞에 넣으면 - 방향으로 이동) (앞에서 빼서 뒤로 넣으면 + 방향으로 이동) 여기서 주의할 점은 덱에서 값을 먼저 꺼내기 때문에 오른쪽으로 이동할때는 한칸 덜 이동해야 제대로 값이 나온다.

 

코드

더보기

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Deque;
import java.util.LinkedList;

public class 풍선_터뜨리기 {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int n = Integer.parseInt(br.readLine());

        Deque<Integer> dq = new LinkedList<>();
        for (int i = 1; i <= n; i++) {
            dq.add(i);
        }
        String[] command = br.readLine().split(" ");

        for (int i = 0; i < n; i++) {
            int next = dq.pollFirst();
            System.out.print(next + " ");
            if (dq.isEmpty())
                break;
            int value = Integer.parseInt(command[next - 1]);
            if (value > 0) {
                value--;
                value = value % dq.size();
                while (!dq.isEmpty() && value > 0) {
                    value--;
                    dq.addLast(dq.pollFirst());
                }
            } else {
                value *= -1;
                value %= dq.size();
                while (!dq.isEmpty() && value > 0) {
                    value--;
                    dq.addFirst(dq.pollLast());
                }
            }
        }
    }
}

처음에 메모리 초과가 걸렸는데 이동하는 횟수가 만약 999번인데 덱 안에 요소가 10개 있다고 가정하면 의미없이 계속 움직이는 경우가 생길 수 있기때문에 이동횟수를 현재 덱의 사이즈 만큼 나눈 나머지만 이동했다. (한바퀴 돌면 그대로니까)

문제:

문자열 S가 주어졌을 때, S의 서로 다른 부분 문자열의 개수를 구하는 프로그램을 작성하시오.

부분 문자열은 S에서 연속된 일부분을 말하며, 길이가 1보다 크거나 같아야 한다.

예를 들어, ababc의 부분 문자열은 a, b, a, b, c, ab, ba, ab, bc, aba, bab, abc, abab, babc, ababc가 있고, 서로 다른것의 개수는 12개이다.

입력:

첫째 줄에 문자열 S가 주어진다. S는 알파벳 소문자로만 이루어져 있고, 길이는 1,000 이하이다.

출력:

첫째 줄에 S의 서로 다른 부분 문자열의 개수를 출력한다.

접근 방법

모든 부분수열을 중복을 제외하고 저장한다.

코드

접기/펼치기

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Set;
import java.util.HashSet;

public class 서로_다른_부분_문자열의_개수 {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String str = br.readLine();
        int len = str.length();
        Set<String> set = new HashSet<>();
        for (int i = 0; i < len; i++) {
            for (int j = i; j <= len; j++) {
                String currentStr = str.substring(i, j);
                if (currentStr != "" && !set.contains(currentStr)) {
                    set.add(currentStr);
                }
            }
        }
        bw.write(String.valueOf(set.size()));
        bw.flush();
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

14888번 연산자 끼워넣기 ( JAVA )  (1) 2024.09.02
2346번 풍선 터뜨리기 ( JAVA )  (0) 2024.09.01
2003번 수들의 합 2 (JAVA)  (2) 2024.08.28
1254번 팰린드롬 만들기 ( JAVA )  (0) 2024.08.27
1822번 차집합 ( JAVA )  (0) 2024.08.26

문제 설명

  • 주어진 배열에서 연속된 수들의 합이 특정 값 N이 되는 부분 수열의 개수를 구하는 문제입니다.

입력 설명

  • 첫 번째로 주어지는 M은 배열의 크기입니다.
  • 두 번째로 주어지는 N은 우리가 찾고자 하는 부분합의 값입니다.
  • 이후 M개의 숫자가 배열에 주어집니다.

알고리즘 설명

  1. 초기화:
    • 배열에서 두 개의 포인터 left와 right를 사용합니다.
    • left는 현재 부분합의 시작을 가리키며, right는 부분합의 끝 다음을 가리킵니다.
    • sum은 left부터 right-1까지의 부분합을 저장합니다.
    • 초기화 시, left는 0에, right는 1에 위치하며, sum은 첫 번째 원소의 값으로 초기화됩니다.
  2. 부분합 탐색:
    • while 루프를 통해 left 포인터가 배열의 끝에 도달할 때까지 반복합니다.
    • 현재 부분합 sum이 과 같은 경우, cnt를 증가시킵니다.
    • 만약 sum이 N보다 작고, right가 배열의 범위를 벗어나지 않았다면, right를 증가시키고 sum에 새로운 원소를 더합니다.
    • 만약 sum이 보다 크거나, right가 더 이상 증가할 수 없다면, sum에서 arr[left]를 빼고 left를 증가시킵니다.
  3. 결과 출력:
    • 위 과정을 통해 부분합이 과 같은 경우를 모두 찾았다면, 그 개수를 출력합니다.
import java.util.Scanner;

public class 수들의_합_2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int M = sc.nextInt();
        int N = sc.nextInt();
        int arr[] = new int[M];
        for (int i = 0; i < M; i++) {
            arr[i] = sc.nextInt();
        }

        int left = 0;
        int right = 1;
        int sum = arr[0];
        int cnt = 0;
        while (left < M) {
            if (sum == N)
                cnt++;
            if (sum < N && right < M) {
                sum += arr[right++];
            } else
                sum -= arr[left++];
        }
        System.out.println(cnt);
    }
}

 

문제

예제 입력

abab

예제 출력

5

접근 방법

  1. 중간 지점 부터 끝지점으로 진행
  2. 현재 위치를 기준으로 팰린드롬인지 확인

코드

import java.util.Scanner;
public class Main
{
    public static boolean isPalindrome(char[] c , int left, int right)
    {

        while(left >=0 && right < c.length)
        {
            if(c[left] != c[right]) return false;

            left--;
            right++;
        }

        if(right < c.length) return false;

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

        String s = sc.nextLine();

        int len = s.length();

        int result = 0;
        for(int i = 0 ; i <= len/2; i++)
        {
            int right = len/2 + i ;
            int left = right - 1;
            if(isPalindrome(s.toCharArray(), left, right))
            {
                result = left+1+ right;
                break;
            }
            if(isPalindrome(s.toCharArray(), left, right+1))
            {
                result = left+right+2;
                break;

            }
        }
        System.out.println(result);

    }
}

'개인 공부 > 백준' 카테고리의 다른 글

11478번 서로 다른 부분 문자열의 개수 ( JAVA )  (2) 2024.08.31
2003번 수들의 합 2 (JAVA)  (2) 2024.08.28
1822번 차집합 ( JAVA )  (0) 2024.08.26
20047번 동전 옮기기  (0) 2024.08.25
2295번 세 수의 합 ( JAVA )  (0) 2024.08.25

문제:

몇 개의 자연수로 이루어진 두 집합 A와 B가 있다. 집합 A에는 속하면서 집합 B에는 속하지 않는 모든 원소를 구하는 프로그램을 작성하시오.

입력:

첫째 줄에는 집합 A의 원소의 개수 n(A)와 집합 B의 원소의 개수 n(B)가 빈 칸을 사이에 두고 주어진다. (1 ≤ n(A), n(B) ≤ 500,000)이 주어진다. 둘째 줄에는 집합 A의 원소가, 셋째 줄에는 집합 B의 원소가 빈 칸을 사이에 두고 주어진다. 하나의 집합의 원소는 2,147,483,647 이하의 자연수이며, 하나의 집합에 속하는 모든 원소의 값은 다르다.

출력:

첫째 줄에 집합 A에는 속하면서 집합 B에는 속하지 않는 원소의 개수를 출력한다. 다음 줄에는 구체적인 원소를 빈 칸을 사이에 두고 증가하는 순서로 출력한다. 집합 A에는 속하면서 집합 B에는 속하지 않는 원소가 없다면 첫째 줄에 0만을 출력하면 된다.

접근 방법

Set 자료구조가 중복을 포함하지 않는 자료구조

B집합으로 Set을 만들고 A의 요소들을 넣으면서 크기가 늘어나는지 확인하는 방법을 사용했다.

코드

접기/펼치기

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Scanner;

public class 차집합
{
    public static void main(String [] args)
    {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        Integer a[] = new Integer[n];
        Integer b[] = new Integer[m];
        for(int i = 0 ; i < n ; i ++) a[i] = sc.nextInt();
        for(int i = 0 ; i < m ; i ++) b[i] = sc.nextInt();

        HashSet<Integer> set = new HashSet<>(Arrays.asList(b));

        ArrayList<Integer> newa = new ArrayList<>();
        int size = set.size();
        int cnt = 0;
        for(int i = 0 ; i < n ; i ++)
        {

            set.add(a[i]);
            if(size != set.size()){
               newa.add(a[i]);
               size ++;
               cnt++;
            }

        }
        Collections.sort(newa);
        System.out.println(cnt);
        for(int tmp : newa)
        {
            System.out.print(tmp+" ");
        }
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

2003번 수들의 합 2 (JAVA)  (2) 2024.08.28
1254번 팰린드롬 만들기 ( JAVA )  (0) 2024.08.27
20047번 동전 옮기기  (0) 2024.08.25
2295번 세 수의 합 ( JAVA )  (0) 2024.08.25
2457번 공주님의 정원 ( JAVA )  (0) 2024.08.24

문제:

100 원짜리 동전과 10 원짜리 동전이 임의의 순서로 한 선 위에 나열되어 있다고 하자. 이제 여기서 ‘두 손가락 이동’ 을 아래와 같이 정의하자.

  • 단계 1: 임의의 두 동전을 선택한다.
  • 단계 2: 단계 1 에서 선택한 두 동전을 둘의 순서를 유지한 채 임의의 위치로 이동한다. (두 동전 모두 제자리에 있거나 두 동전의 순서를 유지한다면 하나만 이동해도 된다.)

‘두 손가락 이동’ 후에도 다른 동전들 간의 순서는 그대로 유지된다. 예를 들어 100 원을 o, 10 원을 x 라 했을 때, 초기에 동전이 oxoxoxxxoo 와 같이 나열되어 있다 하자. 이제 이들 중 굵게 표시된 두 동전을 선택하여 두 손가락 이동을 한번 한 경우, 나올 수 있는 여러 결과들 중에서 네 가지 결과만 아래에 표시했다 (아래 예시에 없는 다른 결과들 또한 나올 수 있음에 유의하자).

  • oxoxoxxxoo
  • oxooxxxxoo
  • oxoxoxxoxo
  • oxoxoxxxoo

n 개의 동전이 나열되어 있는 두 상태 S, T와 함께 두 손가락 이동을 위해 선택할 두 동전의 위치가 주어졌을 때, 한번의 두 손가락 이동을 통해 S에서 T로의 변환이 가능한지 결정하는 프로그램을 작성하시오.

입력:

입력은 표준입력을 사용한다. 첫 번째 줄에 나열된 동전 개수를 나타낸 양의 정수 n (3 ≤ n ≤ 10,000)이 주어진다. 다음 두 줄에 n 개의 동전이 나열된 상태인 ST 가 각각 주어지며, 이 때 STox 로 이루어진 길이가 n 인 문자열로 표현된다 (ox 는 모두 소문자이다). 동전의 위치는 왼쪽에서 오른쪽으로 0부터 n − 1까지 차례대로 번호를 매겨 구분한다. 마지막 줄에는 두 손가락 이동을 위해 선택할 두 동전의 위치 ij가 주어지며, ij 보다 작다 (0 ≤ i < jn − 1).

출력:

출력은 표준출력을 사용한다. 한번의 두 손가락 이동을 통해 S 에서 T로의 변환이 가능한 경우 YES 를, 그렇지 않은 경우 NO 을 출력한다.

접근 방법

시작 문자열과 종료 문자열 2개의 입력을 받는데 시작문자열을 두 손가락 이동으로 종료 문자열 만들기

  1. 시작 문자열을 두 동전을 따로 분리시켜 동전 문자열과 분리된 문자열 2개로 분리한다.
  2. 종료 문자열에 값이 무엇인지 확인하면서 동전문자열과 분리된 문자열 중 사용할 수 있는 문자열의 값을 사용해 종료 문자열을 만든다.

말이 어려워 보이는데 그냥 문자열을 분해시켜서 결과에 맞는 답이 나오는지 확인하는 과정을 거쳤다.
예를 들어
oxoxoxxxo 문자열을 4 번 5 번 인덱스를 따로 분리시켜
동전 문자열 (ox)
분리된 문자열(oxoxxxo)
두개로 oxoxoxxox를 만들어서 가능하면 YES 불가능하면 NO

동전문자열과 분리된문자열 두개다 사용가능할때 처리만 잘해주면 쉽게 풀 수 있는 문제인 것같다.

코드

접기/펼치기

import java.util.Scanner;

public class 동전_옮기기
{
    public static void main(String [] args)
    {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        char[] start = sc.nextLine().toCharArray();
        char[] end = sc.nextLine().toCharArray();
        int x = sc.nextInt();
        int y = sc.nextInt();

        char[] newStr = new char[n-2];
        char[] subStr = new char[]{start[x],start[y]};
        for(int i = 0,j=0 ; i <n-2 ; j++)
        {
            if(j == x || j == y) continue;
            newStr[i++] = start[j];
        }

        int index = 0;
        int subIndex = 0;

        boolean isFind = true;
        for(int i = 0 ; i < n ; i++)
        {
            if(index < n-2 && end[i] == newStr[index] && subIndex < 2 && end[i] == subStr[subIndex]){

                if(index +1 < n-2 && subIndex +1 < 2)
                {

                    if (end[i+1] != newStr[index+1] && end[i+1] == subStr[subIndex+1]) {
                        subIndex++;
                    }
                    else index++;
                }
                else if(subIndex+1 < 2)
                {
                    if(subStr[subIndex+1] == end[i+1]) subIndex++;
                    else index++;
                }
                else
                {
                    index++;
                }

            }

            else if(index < n-2 &&end[i] == newStr[index])
            {
                index ++;
            }
            else if(subIndex < 2 && end[i] == subStr[subIndex])
            {
                subIndex++;
            }
            else
            {
                isFind = false;
                break;
            }
        }
        if(isFind)
        System.out.println("YES");
        else 
        System.out.println("NO");

    }
}

'개인 공부 > 백준' 카테고리의 다른 글

1254번 팰린드롬 만들기 ( JAVA )  (0) 2024.08.27
1822번 차집합 ( JAVA )  (0) 2024.08.26
2295번 세 수의 합 ( JAVA )  (0) 2024.08.25
2457번 공주님의 정원 ( JAVA )  (0) 2024.08.24
3197번 백조의 호수 ( JAVA )  (0) 2024.08.23

문제:

N(5 ≤ N ≤ 1,000)개의 자연수들로 이루어진 집합 U가 있다. 이 중에서 적당히 세 수를 골랐을 때, 그 세 수의 합 d도 U안에 포함되는 경우가 있을 수 있다. 이러한 경우들 중에서, 가장 큰 d를 찾으라.

예를 들어 {2, 3, 5, 10, 18}와 같은 집합이 있다고 하자. 2+3+5 = 10이 되고, 이 수는 집합에 포함된다. 하지만 3+5+10 = 18이 되고, 이 경우가 세 수의 합이 가장 커지는 경우이다.

입력:

첫째 줄에 자연수 N이 주어진다. 다음 N개의 줄에 차례로 U의 원소가 하나씩 주어진다. 주어진 U는 집합이 되므로 입력되는 두 수가 같아서는 안 된다. U의 원소는 200,000,000보다 작거나 같은 자연수이다. 답이 항상 존재하는 경우만 입력으로 주어진다.

출력:

우리가 x번째 수, y번째 수, z번째 수를 더해서 k번째 수를 만들었다라고 하자. 위의 예제에서 2+3+5=10의 경우는 x, y, z, k가 차례로 1, 2, 3, 4가 되며, 최적해의 경우는 2, 3, 4, 5가 된다. k번째 수가 최대가 되도록 하는 것이 목적이다. x, y, z, k가 서로 같아도 된다. 이때, k번째 수를 출력하면 된다.

접근 방법

처음 접근 방식은 백트래킹을 통한 순열을 찾고 이분탐색을 통해 값이 있는지 확인하는 거 였는데 시간초과가 발생했다.

그래서 생각해낸게 중복때문일 것같아서 중복을 제거하는 자료 구조 인 Set을 사용해서 데이터를 저장하고 계산했지만 이 방식또한 시간초과가 발생했다.

그러면 알고리즘이 문제인것같아서 생각을 다시 해보았는데 우선 Set을 사용하여 첫번째 수와 두번째 수를 더 해놓고 합과 세번째 수를 뺀 것과 같으면 합이 정답이 된다는 것을 깨달았다.

  1. 두 수의 합을 중복을 제외하고 저장
  2. 두 수의 차를 구하고 차가 합에 있으면 두 수 중 큰 값이 정답
    예) 1+2 , 6 - 3 이렇게 결과 값이 나오면 6이 정답이 되는 구조

코드

접기/펼치기

import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;

public class 세_수의_합 {

    static int N;
    static long[] arrays;
    static long result = 0;

    public static void findSum() {
        Arrays.sort(arrays);

        HashSet<Long> set = new HashSet<>();

        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                set.add(arrays[i] + arrays[j]);
            }
        }


        for (int k = N - 1; k >= 0; k--) {
            for (int i = 0; i < N; i++) {
                long diff = arrays[k] - arrays[i];
                if (set.contains(diff)) {
                    result = arrays[k];
                    return;
                }
            }
        }
    }

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

        N = sc.nextInt();
        arrays = new long[N];
        for (int i = 0; i < N; i++) {
            arrays[i] = sc.nextInt();
        }

        findSum();
        System.out.println(result);
    }
}

생각보다 어려웠다. 실력이 많이 부족한것같다.

'개인 공부 > 백준' 카테고리의 다른 글

1822번 차집합 ( JAVA )  (0) 2024.08.26
20047번 동전 옮기기  (0) 2024.08.25
2457번 공주님의 정원 ( JAVA )  (0) 2024.08.24
3197번 백조의 호수 ( JAVA )  (0) 2024.08.23
13549번 숨바꼭질 3 ( JAVA )  (1) 2024.08.22

문제:

오늘은 공주님이 태어난 경사스러운 날이다. 왕은 이 날을 기념하기 위해 늘 꽃이 피어있는 작은 정원을 만들기로 결정했다.

총 N개의 꽃이 있는 데, 꽃은 모두 같은 해에 피어서 같은 해에 진다. 하나의 꽃은 피는 날과 지는 날이 정해져 있다. 예를 들어, 5월 8일 피어서 6월 13일 지는 꽃은 5월 8일부터 6월 12일까지는 꽃이 피어 있고, 6월 13일을 포함하여 이후로는 꽃을 볼 수 없다는 의미이다. (올해는 4, 6, 9, 11월은 30일까지 있고, 1, 3, 5, 7, 8, 10, 12월은 31일까지 있으며, 2월은 28일까지만 있다.)

이러한 N개의 꽃들 중에서 다음의 두 조건을 만족하는 꽃들을 선택하고 싶다.

  1. 공주가 가장 좋아하는 계절인 3월 1일부터 11월 30일까지 매일 꽃이 한 가지 이상 피어 있도록 한다.
  2. 정원이 넓지 않으므로 정원에 심는 꽃들의 수를 가능한 적게 한다.

N개의 꽃들 중에서 위의 두 조건을 만족하는, 즉 3월 1일부터 11월 30일까지 매일 꽃이 한 가지 이상 피어 있도록 꽃들을 선택할 때, 선택한 꽃들의 최소 개수를 출력하는 프로그램을 작성하시오.

입력:

첫째 줄에는 꽃들의 총 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 다음 N개의 줄에는 각 꽃이 피는 날짜와 지는 날짜가 주어진다. 하나의 날짜는 월과 일을 나타내는 두 숫자로 표현된다. 예를 들어서, 3 8 7 31은 꽃이 3월 8일에 피어서 7월 31일에 진다는 것을 나타낸다.

출력:

첫째 줄에 선택한 꽃들의 최소 개수를 출력한다. 만약 두 조건을 만족하는 꽃들을 선택할 수 없다면 0을 출력한다.

접근 방법

오랜만에 그리디 문제를 풀고 싶어서 골랐는데 호되게 당했다.

처음에는 월, 일을 일로 바꾸면 365일로 바뀌게 되고 1~365일 중에 60일 (3월 1일)과 334일(11월 30일) 범위에 해당하는 값중에 큰 값부터 소거하는 계획을 세웠는데 하다가 꼬여버렸다.

그래서 바뀐 접근 방법으로는

  1. 60일(3월 1일) 부터 시작해 최대한 길게 연결된 범위를 선택
  2. 최대한 긴 범위가 선택되고 그 마지막위치가 다시 시작점이 되어 최대한 긴 범위를 선택

코드

접기/펼치기

import java.util.Scanner;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class 공주님의_정원{

    static int monthToDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};
    public static void main(String [] args)
    {
        Scanner sc = new Scanner(System.in);
        int numberOfFlower = sc.nextInt();

        ArrayList<int[]> listOfFlower = new ArrayList();

        for(int i = 0 ; i < numberOfFlower ; i++)
        {
            int startMonth = sc.nextInt();
            int startDay = sc.nextInt();
            int endMonth = sc.nextInt();
            int endDay = sc.nextInt();

            int startPoint = convertToDayOfYear(startMonth, startDay);
            int endPoint = convertToDayOfYear(endMonth, endDay);

            if(startPoint <= 60) startPoint = 60;
            if(endPoint >334) endPoint = 335;

            listOfFlower.add(new int[]{startPoint,endPoint});
        }

        Comparator<int[]> comparator = (arr1, arr2) ->{
            if(arr1[0]==arr2[0])
            {
                return Integer.compare(arr2[1],arr1[1]);
            }
            return Integer.compare(arr1[0], arr2[0]);
        };

        Collections.sort(listOfFlower, comparator);

        int currentDay = 60;
        int endDay = 60;
        int count = 0;
        int index = 0;

        while(currentDay < 335)
        {
            boolean found = false;
            while(index < listOfFlower.size() && listOfFlower.get(index)[0] <= currentDay)
            {
                if(listOfFlower.get(index)[1] >= endDay)
                {
                    endDay = listOfFlower.get(index)[1];
                    found = true;
                }
                index++;
            }
            if(found)
            {
                count++;
                currentDay = endDay;
            }
            else{
                break;
            }
        }
            if(currentDay>=335)
            {
                System.out.println(count);
            }
            else{
                System.out.println(0);
            }

    }
    static int convertToDayOfYear(int month, int day) {
        int dayOfYear = day;
        for (int i = 0; i < month - 1; i++) {
            dayOfYear += monthToDays[i];
        }
        return dayOfYear;
    }
}

'개인 공부 > 백준' 카테고리의 다른 글

20047번 동전 옮기기  (0) 2024.08.25
2295번 세 수의 합 ( JAVA )  (0) 2024.08.25
3197번 백조의 호수 ( JAVA )  (0) 2024.08.23
13549번 숨바꼭질 3 ( JAVA )  (1) 2024.08.22
16194번 카드 구매하기 2 ( JAVA )  (0) 2024.08.19

+ Recent posts