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

 

2839번: 설탕 배달

상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그

www.acmicpc.net

 

 

[정답 코드1]

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

public class Main {
    public static void main(String[] args) throws Exception {
        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 count = 0;
        while (N > 0) {
            if (N % 5 == 0) { // N이 5로 나눠질수 있을 경우 5로 나눠버린 후 출력
                count += N / 5; // 5로 나눈 몫을 count 에 더함
                System.out.println(count);
                return;
            }
            if (N < 3) {
                System.out.println("-1"); // N이 3보다 적을 경우에는 -1
                return;
            }
            N-=3; // N이 5로 안나눠질 경우에는 3kg 짜리 하나를 만듬
            count++;
        }
        System.out.println(count);
    }
}

 

[설명]

처음에 작성했던 코드이다. 정말 단순하게 N이 5로 나눠질 때 까지 3씩 빼면서 count 를 늘렸다. 풀고나서 다른사람들이 작성했던 코드를 봤을때 깜짝놀랐었다. 이렇게 단순하게 푸는게 아닌 수학적인 방법을 사용하면 코드도 더 간결하고 쉽게 풀 수 있엇다.

 

[정답 코드 2]

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

public class Main {
    public static void main(String[] args) throws Exception {
        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());

        if (N == 4 || N == 7) { // N이 4이거나 7일 경우에는 3이나 5로 나눌수 없다.
            System.out.println(-1);
        } else if (N % 5 == 0) { // N이 5로 바로 나눠질 경우
            System.out.println(N / 5);
        } else if (N % 5 == 1 || N % 5 == 3) { // N이 6, 8, 11, 13, 16 등일 경우
            System.out.println((N / 5) + 1);
        } else if (N % 5 == 2 || N % 5 == 4) { // N이 9, 12, 14, 17, 19 등일 경우
            System.out.println((N / 5) + 2);
        }
    }
}

 

[설명]

다른 사람이 작성한 글을 보며 대단하다고 생각했다. 조건문이 4개가 있는데 먼저

N이 4 또는 7일 경우에는 3kg 포대와 5kg 포대를 아무리 사용해도 나눠지지 않기 때문에 "-1" 을 출력해준다.

혹은 N이 5로 나눠질 경우에는 N/5 를 출력해준다.

혹은 N이 6, 8, 11, 13, 16 등 N%5 의 값이 1 또는 3일 경우 (N / 5) + 1 을 출력해준다.

혹은 N이 9, 12, 14, 17, 19 등 N%5의 값이 2 또는 4 일 경우 (N / 5) + 2 를 출력해준다.

자세한 설명은 참고한 블로그를 링크로 달아놓겠다.

 

참고한 블로그 : 

https://st-lab.tistory.com/72

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

 

14503번: 로봇 청소기

첫째 줄에 방의 크기 $N$과 $M$이 입력된다. $(3 \le N, M \le 50)$  둘째 줄에 처음에 로봇 청소기가 있는 칸의 좌표 $(r, c)$와 처음에 로봇 청소기가 바라보는 방향 $d$가 입력된다. $d$가 $0$인 경우 북쪽

www.acmicpc.net

 

[정답 코드]

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

public class Main {
    static int N, M;
    static int[][] room;
    static int[] dx = {-1, 0, 1, 0};
    static int[] dy = {0, 1, 0, -1}; // 북, 동, 남, 서 순서
    static int count = 0;
    static boolean flag;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        st = new StringTokenizer(br.readLine(), " ");

        int robotX = Integer.parseInt(st.nextToken());
        int robotY = Integer.parseInt(st.nextToken());
        int robotHead = Integer.parseInt(st.nextToken());
        room = new int[N][M];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            for (int j = 0; j < M; j++) {
                room[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        clean(robotX, robotY, robotHead);
        System.out.println(count);
    }

    static void clean(int x, int y, int head) {
        // 현재 위치가 더럽다면 청소
        if (room[x][y] == 0) {
            room[x][y] = 2; // 현재 위치 청소
            count++;
        }
        flag = false;
        for (int i = 0; i < 4; i++) {
            int nextHead = (head + 3) % 4; // 반시계 방향으로 90도 회전한 방향이 청소할 구역인지
            int nextX = x + dx[nextHead];
            int nextY = y + dy[nextHead];
            if (nextX >= 0 && nextY >= 0 && nextX < N && nextY < M) {
                if (room[nextX][nextY] == 0) {
                    clean(nextX, nextY, nextHead);
                    flag = true; // 청소할 구역이 있는 경우
                    break;
                }
            }
            head = (head + 3) % 4; // 반시계 90도 회전
        }


        if (!flag) { // 네 방향이 모두 청소된곳이거나 벽인 경우
            int nextHead = (head + 2) % 4; // 후진
            int nextX = x + dx[nextHead];
            int nextY = y + dy[nextHead];
            if (nextX >= 0 && nextY >= 0 && nextX < N && nextY < M) { // 후진할 공간이 있을 경우
                if (room[nextX][nextY] != 1) {
                    clean(nextX, nextY, head); // 후진
                }
            }
        }
    }
}

 

[설명]

이 문제는 4방탐색을 활용한 구현문제이다.

문제 설명이 이상해 이해하기 어렵지만 간단하게 정리하자면

1. 현재 위치를 청소한다.

2. 바라보는 방향에서 반시계 90도로 회전하면 청소할 수 있는지 탐색한다.

3 - 1. 청소할 수 있다면 전진한다. 그 후 1번으로 간다.

3 - 2. 청소할 수 없다면 후진한다.

4 - 1. 후진할 수 있다면 1번으로 간다.

4 - 2. 후진할 수 없다면 종료한다.

 

먼저 방의 로봇청소기의 위치와 방향을 clean 함수에 넣고 실행시킨다.

clean 함수는 로봇청소기의 위치가 청소해야할곳이라면 청소를 해 해당 위치를 임의의 숫자 2로 변경해준다.

그 후 4방탐색을 실시하는데 중요한 점은 4방탐색을 할 때 내가 바라보는 방향에서 반시계로 90도 씩 돌아가는 순서대로 탐색을 해야하는 것이다.

(head + 3) % 4 를 했을 경우 내가 바라보는 방향 head에서 반시계로 90도 돌게된다. 그리고 이를 4번할 경우 다시 원래방향을 바라보게 된다.

예를들어 head 가 0(북쪽) 일경우

(0 + 3) % 4 = 3(서쪽)

(3 + 3) % 4 = 2(남쪽)

(2 + 3) % 4 = 1(동쪽)

(1 + 3) % 4 = 0(북쪽)

이렇게 네 방향을 탐색했는데 모두 청소된 곳이거나 벽일경우 

(head + 2) % 4 를 하게되면 180도 뒤를 바라보게 된다.

만약 head 가 0(북쪽) 일경우

(0 + 2) % 4 = 2(남쪽)

이므로 후진을 할 수 있다.

 

'코딩테스트' 카테고리의 다른 글

백준 1463번: 1로 만들기[JAVA]  (0) 2024.02.02
백준 2839번: 설탕 배달[JAVA]  (1) 2024.02.02
백준 13335번: 트럭[JAVA]  (1) 2024.02.01
백준 5212번: 지구 온난화[JAVA]  (1) 2024.02.01
백준 20310번: 타노스[JAVA]  (1) 2024.01.30

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

 

13335번: 트럭

입력 데이터는 표준입력을 사용한다. 입력은 두 줄로 이루어진다. 입력의 첫 번째 줄에는 세 개의 정수 n (1 ≤ n ≤ 1,000) , w (1 ≤ w ≤ 100) and L (10 ≤ L ≤ 1,000)이 주어지는데, n은 다리를 건너는 트

www.acmicpc.net

 

[정답 코드]

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

public class Main {
    public static void main(String[] args) throws Exception {
        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 w = Integer.parseInt(st.nextToken());
        int L = Integer.parseInt(st.nextToken());

        Queue<Integer> truck = new LinkedList<>();
        Queue<Integer> bridge = new LinkedList<>();
        int count = 0;
        int bridgeWeight = 0;
        st = new StringTokenizer(br.readLine(), " ");

        for (int i = 0; i < n; i++) {
            truck.add(Integer.parseInt(st.nextToken())); // 트럭의 무게를 큐에 담음
        }

        for (int i = 0; i < w; i++) {
            bridge.add(0); // 현재 다리에 올라와있는 트럭의 무게
        }

        while (!bridge.isEmpty()) { // 현재 다리에 올라와 있는 트럭이 존재하지 않을 때 까지
            count++; // 로직이 진행될 때 마다 시간이 1씩 증가
            bridgeWeight-=bridge.poll(); // 다리에 올라와있는 트럭이 한대 씩 다리에서 내려옴 , queue.poll() : 큐의 맨 앞의 값 추출
            if (!truck.isEmpty()) { // 큐에 담긴 트럭이 존재하지 않을 때 까지
                if (truck.peek() + bridgeWeight <= L) { // 트럭큐에 담긴 맨앞의 값과 현재 다리위에 올라와있는 트럭의 무게의 합이 L 보다 낮을때 , queue.peek() : 큐의 맨 앞의 값 확인
                    bridgeWeight += truck.peek();
                    bridge.add(truck.poll()); // 다리에 한 대 씩 올라감
                } else {
                    bridge.add(0); // 트럭큐에 트럭이 존재하지만 다리의 최대하중 때문에 다리로 못올라갈 경우 truck.poll()은 하지 않는다.
                }
            }
        }
        System.out.println(count);

    }
}

 

[설명]

이 문제는 큐를 활용하면 쉽게 풀 수 있다. 먼저 다리위에 트럭이 몇개가 존재하는지를 담는 큐 bridge 를 하나 만들고, 다리를 건너기 전의 트럭을 담는 truck 큐를 하나 만들어준다.

bridge 큐가 빌때까지 반복한다. 반복문이 한번 반복될때마다 트럭이 다리위에서 한칸씩 전진하는 구조이다. 

한칸 전진할 때마다 truck 큐를 확인하며 truck 큐를 하나 꺼내 다리위에 올라갔을 때 최대하중을 넘는지 안넘는지 체크하고 넘지않는다면 bridge 큐에 넣고 넘는다면 bridge에 0을 집어 넣는다. 

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

 

5212번: 지구 온난화

첫째 줄에 지도의 크기 R과 C (1 ≤ R, C ≤ 10)가 주어진다. 다음 R개 줄에는 현재 지도가 주어진다.

www.acmicpc.net

 

 

[정답 코드]

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

public class Main {
    static int[] dx = {0, 0, 1, -1};
    static int[] dy = {1, -1, 0, 0};
    static char[][] graph; // 지도
    static int count; // 섬 근처의 바다의 수
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        int R = Integer.parseInt(st.nextToken());
        int C = Integer.parseInt(st.nextToken());
        graph = new char[R][C];
        int up = R; // 지도의 가장 위
        int down = 0; // 지도의 가장 아래
        int left = C; // 지도의 가장 왼쪽
        int right = 0; // 지도의 가장 아래쪽

        for (int i = 0; i < R; i++) {
            String input = br.readLine();
            for (int j = 0; j < C; j++) {
                graph[i][j] = input.charAt(j);
            }
        }

        for (int i = 0; i < R; i++) {
            for (int j = 0; j < C; j++) {
                if (graph[i][j] == 'X') {
                    count = 0;
                    for (int k = 0; k < 4; k++) { // 우 좌 하 상 순서 4방 탐색
                        int nx = i + dx[k];
                        int ny = j + dy[k];
                        if (nx >= 0 && ny >= 0 && nx < R && ny < C) { // 지도 안에 있을경우
                            if (graph[nx][ny] == '.') { // 근처가 바다일 경우
                                count++;
                            }
                        } else { // 지도의 바깥부분도 바다
                            count++;
                        }
                    }
                    if (count >= 3) { // 바다가 3면 이상일 경우
                        graph[i][j] = 'S'; // 임의의 값 S 로 변경
                    }
                }
                if (graph[i][j] == 'X') { // 지도의 가장 위, 아래, 왼쪽, 오른쪽 의 좌표를 갱신
                    up = Math.min(up, i);
                    down = Math.max(down, i);
                    left = Math.min(left, j);
                    right = Math.max(right, j);
                }
            }
        }

        // 새로운 지도를 그려줌
        for (int i = up; i <= down; i++) {
            for (int j = left; j <= right; j++) {
                char c = graph[i][j];
                if (c == 'X') {
                    bw.write(c);
                } else {
                    bw.write('.');
                }
            }
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

 

[설명]

4방탐색을 활용한 구현문제이다. 지도를 탐색하다가 섬을 만나면 4방탐색을 실시한다. 4방 탐색 dx/dy 기법을 활용해준다.

4방탐색을 하면서 바다의 수를  카운팅해 바다가 3면이상이면 섬을 지도에서 지워준다. 

지도를 탐색해 나아가면서 모든 섬의 좌표를 체크해 지도의 가장 위에 있는 섬, 아래있는 섬.. 등의 좌표를 갱신해 나간다.

탐색이 끝났다면 가장 바깥 부분의 섬의 좌표들을 활용해 새로운 지도를 그려준다.

'코딩테스트' 카테고리의 다른 글

백준 14503번: 로봇 청소기[JAVA]  (1) 2024.02.02
백준 13335번: 트럭[JAVA]  (1) 2024.02.01
백준 20310번: 타노스[JAVA]  (1) 2024.01.30
백준 19941번: 햄버거 분배[JAVA]  (0) 2024.01.29
백준 21921번: 블로그 [JAVA]  (0) 2024.01.28

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

 

20310번: 타노스

어느 날, 타노스는 0과 1로 이루어진 문자열 $S$를 보았다. 신기하게도, $S$가 포함하는 0의 개수와 $S$가 포함하는 1의 개수는 모두 짝수라고 한다. 갑자기 심술이 난 타노스는 $S$를 구성하는 문자

www.acmicpc.net

 

 

[25점 정답 코드]

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

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

        char[] words = br.readLine().toCharArray();
        int countZero = 0;
        int countOne = 0;

        for (char word : words) {
            if (word == '0') {
                countZero++; // 0의 개수를 카운팅
            } else {
                countOne++; // 1의 개수를 카운팅
            }
        }
        for (int i = 0; i < countZero / 2; i++) { // 0의 개수의 절반을 날린것을 먼저 출력
            bw.write("0");
        }
        for (int i = 0; i < countOne / 2; i++) { // 1의 개수의 절반을 날린것을 뒤에 출력
            bw.write("1");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

 

[100점 정답 코드]

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

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

        String words = br.readLine();
        int countZero = 0;
        int countOne = 0;

        for (int i = 0; i < words.length(); i++) {
            if (words.charAt(i) == '0') { // 0의 개수 카운팅
                countZero++;
            } else { // 1의 개수 카운팅
                countOne++;
            }
        }
        countZero/=2; // 0의 개수 카운팅 한것의 절반을 날림
        countOne/=2; // 1의 개수 카운팅 한것의 절반을 날림
        
        // 1 제거 로직
        int i = 0;
        while (countOne != 0) {
            if (words.charAt(i) == '1') { // 해당 인덱스의 가 1일 경우
                words = words.substring(0, i) + words.substring(i + 1); // i번 인덱스를 제거한다
                countOne--;
                i = -1; // i번 인덱스를 제거할 경우 i를 다시 초기화 해주어야 인덱스가 꼬이지 않는다
            }
            i++;
        }

        // 0 제거 로직
        int j = words.length() - 1;
        while (countZero != 0) {
            if (words.charAt(j) == '0') { // 해당 인덱스의 가 0일 경우
                words = words.substring(0, j) + words.substring(j + 1); // j번 인덱스를 제거한다
                countZero--;
                j = words.length(); // j번 인덱스를 제거할 경우 j를 다시 초기화 해주어야 인덱스가 꼬이지 않는다
            }
            j--;
        }
        System.out.println(words);

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

 

[설명]

이 문제는 그리디 알고리즘과 문자열을 잘 다뤄야한다. 먼저 처음에 25점 짜리 정답은 문제를 잘못이해해 0과 1의 개수를 카운팅 한후 0과1을 단순 재배열해 0의 개수의 절반만큼 0출력 후 나머지 1의 개수의 절반만큼 1출력했다. 그 결과 25점짜리 정답을 맞았다.

다시 문제를 보니 입력받은 문자열을 다 뜯어서 재배치하는것이 아니라 입력받은 문자열에서 0과 1을 제거해 사전순으로 출력하는것이다.

1을 제거하는 로직을 보면 for문이 아닌 while문으로 작성한 이유는 for문으로 작성할 경우 String.substring() 으로 해당 인덱스의 문자를 제거할 경우 제거한 인덱스만큼 문자열이 줄어들어 i가 바라보는 인덱스와 검사해야할 문자열의 인덱스가 꼬이는 경우가 발생하기 때문에 while문으로 작성했다. String.substring()으로 문자열을 수정했을 경우 i를 초기화시켜 다시 처음부터 문자열을 체크했다. 지금보니 문자열을 i = -1 이런식으로 처음으로 초기화시킬 필요없이 수정한 인덱스 바로 뒤로 초기화시켜도 되는것같다.

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

 

19941번: 햄버거 분배

기다란 벤치 모양의 식탁에 사람들과 햄버거가 아래와 같이 단위 간격으로 놓여 있다. 사람들은 자신의 위치에서 거리가 $K$ 이하인 햄버거를 먹을 수 있다. 햄버거 사람 햄버거 사람 햄버거 사

www.acmicpc.net

 

[정답 코드]

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

public class Main {
    public static void main(String[] args) throws Exception {
        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());
        char[] arr = br.readLine().toCharArray();
        int count = 0;
        for (int i = 0; i < N; i++) {
            if (arr[i] == 'P') { // 사람일 경우
                int startIndex = Math.max(i - K, 0); // 0보다 작으면 안됨
                int endIndex = Math.min(i + K, N - 1); // 배열의 끝을 넘어가면 안됨
                for (int j = startIndex; j <= endIndex; j++) {
                    if (arr[j] == 'H') {
                        count++;
                        arr[j] = 'X'; // 햄버거가 있다면 햄버거자리를 임의의 문자로 대체
                        break;
                    }
                }
            }

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

 

[설명]

이 문제는 그리디 알고리즘 문제이다. 슬라이딩 윈도우 처럼 시작인덱스와 끝인덱스를 설정한 후 이동해 나아가면 햄버거를 찾고, 햄버거를 찾았다면 다른 사람이 햄버거를 중복으로 찾는 경우가 발생할 수 있기 때문에 햄버거를 찾았다면 임의의 문자로 대체하고 break로 빠져나온다.

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

 

21921번: 블로그

첫째 줄에 $X$일 동안 가장 많이 들어온 방문자 수를 출력한다. 만약 최대 방문자 수가 0명이라면 SAD를 출력한다. 만약 최대 방문자 수가 0명이 아닌 경우 둘째 줄에 기간이 몇 개 있는지 출력한다

www.acmicpc.net

 

[틀린 코드]

더보기

 

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

public class Main {
    public static void main(String[] args) throws Exception {
        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 X = Integer.parseInt(st.nextToken());
        int[] visitors = new int[N];
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < N; i++) {
            visitors[i] = Integer.parseInt(st.nextToken());
        }


        int max = 0;
        int answer = 0;
        for (int i = 0; i <= N - X; i++) {
            int tmp = 0;
            for (int j = i; j < i + X; j++) { // X기간동안의 방문자 수의 합
                tmp += visitors[j];
            }
            if (tmp > max) { // X기간동안 최고 방문자수가 갱신될 경우
                answer = 0;
                max = Math.max(tmp, max);
            }
            if (tmp == max) { // 최고방문자수가 max 와 같은 기간 카운팅
                answer++;
            }
        }
        System.out.println(max);
        System.out.println(answer);
    }
}

 

[정답 코드]

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

public class Main {
    public static void main(String[] args) throws Exception {
        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 X = Integer.parseInt(st.nextToken());
        int[] visitors = new int[N];
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < N; i++) {
            visitors[i] = Integer.parseInt(st.nextToken());
        }

        int sum = 0;
        for (int i = 0; i < X; i++) {
            sum += visitors[i];
        }


        int max = sum;
        int answer = 1;
        for (int i = 0; i < N - X; i++) { // 슬라이딩 윈도우

            //한칸 씩 이동
            sum += visitors[i+X];
            sum -= visitors[i];

            if (sum == max) { // 최다 방문자수가 유지될 경우 answer++
                answer++;
            }

            if (sum > max) { // 최다 방문자수가 갱신되었을 경우
                answer = 1;
                max = sum;
            }

        }
        if (max == 0) { // 최대 방문자 수가 0이라면 SAD 출력
            System.out.println("SAD");
        } else {
            System.out.println(max);
            System.out.println(answer);
        }
    }
}

 

[설명]

이 문제는 슬라이딩 윈도우 기법을 활용해야 한다. 처음에 이 문제를 풀었을때는 이중 for문으로 문제를 풀었었다. 그러자 시간초과 떴다. 그래서 2중for문을 해결하기 위해 for문을 하나로 두고 다시 풀었다.

먼저 입력을 받고 입력받은것중에 슬라이딩 윈도우로직을 사용할 구간만큼 미리 합을 구해놓는다.

그리고 for문을 이용해 한칸씩 이동해 나아가며 합을 계산한다. 

계산한 합이 알고있는 max값보다 클 경우 max값을 갱신하고 answer를 1로 초기화시킨다.

max값과 새로 이동해 계산한 합의 값이 같을 경우 answer를 1 증가시킨다.

 

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

 

20920번: 영단어 암기는 괴로워

첫째 줄에는 영어 지문에 나오는 단어의 개수 $N$과 외울 단어의 길이 기준이 되는 $M$이 공백으로 구분되어 주어진다. ($1 \leq N \leq 100\,000$, $1 \leq M \leq 10$) 둘째 줄부터 $N+1$번째 줄까지 외울 단

www.acmicpc.net

 

 

[정답 코드]

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        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 M = Integer.parseInt(st.nextToken());
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < N; i++) {
            String word = br.readLine();
            if (word.length() >= M) { // 길이 M 이상만 저장
                map.put(word, map.getOrDefault(word, 0) + 1); // 같은 단어가 등록될경우 value를 하나씩 증가
            }
        }
        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare == 0 ? o1.getKey().compareTo(o2.getKey()) : lengthCompare; // key의 길이가 같을경우(lengthCompare == 0일경우) 길이가 같은것은 사전순으로 정렬
                }
                return cmp;
            }
        });
        for (Map.Entry<String, Integer> entry : entryList) {
            bw.write(entry.getKey());
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();
    }
}

 

[설명]

이 문제는 맵을 활용한 키와 밸류의 정렬, Comparator를 잘 활용해야하는 문제이다.

먼저 단어를 입력받을때 단어의 길이가 M 이상인 경우만 Map에 저장해준다. 같은 단어가 나올때 해당 단어의 갯수를 map으로 카운팅 해준다.

입력이 끝났다면 map의 Value를 기준으로 내림차순 정렬을 해준다. 그렇다면 

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                return cmp;
            }
        });

이런식으로 정렬을 해주면 된다.

 

하지만 문제에서 value 가 같을 경우에는 길이가 긴순으로 재 정렬을 해줘야한다고 되어있다. 그래서 약간 변형해

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare; 
                }
                return cmp;
            }
        });

 cmp 가 같을 경우에 조건문을 걸어 길이가 긴순으로 내림차순을 해준다.

 

하지만 문제에 또다른 조건이 있다. 길이마저 같을 경우에는 사전순으로 정렬을 해준다고 되어있다.

        // map 의 value 기준 내림차순 정렬
        List<Map.Entry<String, Integer>> entryList = new LinkedList<>(map.entrySet());
        entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int cmp = o2.getValue().compareTo(o1.getValue()); // value 기준 내림차순 정렬 0일경우 value가 같음
                if (cmp == 0) {
                    int lengthCompare = Integer.compare(o2.getKey().length(), o1.getKey().length()); // value가 같을 경우 value가 같은것 끼리 key의 길이를 내림차순 정렬
                    return lengthCompare == 0 ? o1.getKey().compareTo(o2.getKey()) : lengthCompare; // key의 길이가 같을경우(lengthCompare == 0일경우) 길이가 같은것은 사전순으로 정렬
                }
                return cmp;
            }
        });

3항연산자를 사용해 lengthComapre가 같을경우에는 o1.getKey().compareTo(o2.getKey()) 로 비교해사전순으로 정렬해준다. 

그리고 마지막 출력을 할 때

        for (Map.Entry<String, Integer> entry : entryList) {
        	System.out.println(entry.getKey());
        }

이런식으로 출력을 할 경우 시간초과가 발생한다. 문제설명 맨 마지막에 설명을 보듯 BufferedWriter를 사용해주어야 시간초과가 발생하지 않는다.

 

BufferedWriter를 사용해 다시 코드를 작성한다면

        for (Map.Entry<String, Integer> entry : entryList) {
            bw.write(entry.getKey());
            bw.write("\n");
        }
        bw.flush();
        bw.close();
        br.close();

이런식으로 출력해 주어야 한다.

+ Recent posts