Problem Solving (67)

문제 링크

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

풀이

맵에 <사이트, 비밀번호>로 저장하고 찾으면 된다!

혹시나 더 빠르게 푸는 방법이 있나 싶어 백준에 정답 기록을 봤는데 비슷하게 푼 것 같다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(tokens.nextToken());
        int M = Integer.parseInt(tokens.nextToken());

        HashMap<String, String> map = new HashMap<>();

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            map.put(tokens.nextToken(), tokens.nextToken());
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < M; i++) {
            sb.append(map.get(br.readLine())).append("\n");
        }
        System.out.print(sb);
    }

}

문제 링크

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

풀이

처음에는 조합으로 3개를 선택해서 풀었는데 N이 100,000까지 들어오기 때문에 시간 초과가 난다.

심리적 거리가 0이 될 경우는 모두 같은 mbti인 경우이고, N이 32를 넘는 경우 무조건 심리적 거리가 0이 된다.

왜냐하면 N이 32인 경우 서로 다른 mbti가 입력으로 들어왔을 경우 최소 2개씩 중복되는 mbti가 나온다.

따라서 N이 33이라면 2개로 중복된 mbti가 3개가 되기 때문에 이 때 심리적 거리는 0이 된다.

그래서 입력을 받고 N이 32를 넘는 경우에는 검사를 하지 않고 결과를 0으로 출력한다.

N이 32를 넘지 않는 경우에는 조합으로 3개를 선택해서 심리적 거리를 계산한다.

코드

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

public class Main {
    static int res;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens;
        StringBuilder sb = new StringBuilder();

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

        while (T-- > 0) {
            int N = Integer.parseInt(br.readLine());
            tokens = new StringTokenizer(br.readLine());

            //32개가 넘으면 무조건 0이 됨. 이미 같은 유형이 2개씩 존재하기 때문
            if (N > 32) {
                sb.append(0).append("\n");
                continue;
            }

            String[] type = new String[N];
            for (int i = 0; i < N; i++) {
                type[i] = tokens.nextToken();
            }

            //조합으로 선택
            res = Integer.MAX_VALUE;
            comb(0, 0, new String[3], type, N);
            sb.append(res).append("\n");

        }
        System.out.print(sb);
    }

    private static void comb(int cnt, int start, String[] s, String[] type, int N) {
        if (cnt == 3) {
            int temp = 0;
            for (int i = 0; i < 4; i++) {
                if (s[0].charAt(i) != s[1].charAt(i)) temp++;
                if (s[0].charAt(i) != s[2].charAt(i)) temp++;
                if (s[1].charAt(i) != s[2].charAt(i)) temp++;
            }
            res = Math.min(res, temp);
            return;
        }

        for (int i = start; i < N; i++) {
            s[cnt] = type[i];
            comb(cnt+1, i+1, s, type, N);
        }
    }
}

'Problem Solving > BOJ' 카테고리의 다른 글

[백준 Java] 9019 DSLR  (0) 2023.06.15
[백준 Java] 17219 비밀번호 찾기  (0) 2023.06.15
[백준 Java] 14890 경사로  (0) 2023.06.10
[백준 Java] 14503 로봇 청소기  (0) 2023.06.09
[백준 Java] 13458 시험 감독  (0) 2023.06.01

문제 링크

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

 

풀이

SW Expert Academy에서 풀었던 문제이다. 활주로 건설과 같은 문제인데 그때에는 행과 열에 대해서 모두 조사하는 방식이었지만, 이번에는 맵 2개를 선언하여 하나의 맵은 90도 회전한 맵으로 저장하고 두 맵을 한 행씩 조사하는 로직으로 변경했다.

 

한 행을 검사할 때, 행의 첫번째 값과 카운트를 갱신해둔다.

두번째 요소부터 검사할때 첫번째 행의 값과 같다면 카운트를 증가시키고 넘어간다.

이전 값과 다를 때 차이가 1이 아니라면 이 행은 경사로를 세울 수 없어 false를 리턴한다.

이전값과의 차이가 1이라면 높이가 올라가는 경우, 아니라면 낮아지는 경우에 따라 경사로를 세울 수 있는 조건 확인을 다르게 한다.

높아지는 경우, L만큼 공간을 만들 수 있는지 확인하고 만들 수 없다면 false, 만들 수 있다면 cnt와 prev 값을 갱신한다.

낮아지는 경우, 지금 위치에서 L만큼 위치까지 전부 같은 값인지 확인한다. 다른 값이 있다면 false, 전부 같은 값이라면 경사로를 만들 수 있고, L만큼 만든 다음 위치로 인덱스와 cnt를 조절한다. 이때 이 곳은 경사로를 만들었기 때문에 cnt는 0으로 설정한다.

 

코드

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

public class Main {
    static int N, L, res, map[][], map2[][];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken());
        L = Integer.parseInt(tokens.nextToken());
        map = new int[N][N];
        map2 = new int[N][N]; //i,j위치 반전시킨 맵

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(tokens.nextToken());
                map2[j][i] = map[i][j];
            }
        }

        res = 0;

        //기본 맵 행 검사, 회전한 맵 행 검사
        for (int i = 0; i < N; i++) {
            if (checkRow(map[i])) {
                res++;
            }
            if(checkRow(map2[i])){
                res++;
            }
        }

        System.out.println(res);
    }

    private static boolean checkRow(int[] rows) {
        //높이가 달라졌을 때 경사로를 생성할 수 있는지 확인
        int prev = rows[0];
        int cnt = 1;
        for (int i = 1; i < N; i++) {
            if (prev == rows[i]) { //높이가 같은 경우 그냥 증가
                cnt++;
                continue;
            }
            //높이가 다를 때
            if (Math.abs(prev - rows[i]) != 1) {
                //높이 차이가 1이 아니라면 경사로 만들지 못함
                return false;
            }
            //높이 차이가 1일 때
            //높아지는 경우 -> L만큼 공간을 만들 수 있는지 확인
            if (rows[i] - prev == 1) {
                if (cnt >= L) {
                    //경사로 생성
                    cnt = 1;
                    prev = rows[i];
                } else {
                    return false;
                }
            } else {
                //낮아지는 경우
                boolean flag = true;
                for (int j = i + 1; j < i + L; j++) {
                    if (!(j < N && rows[j] == rows[i])) {
                        flag = false;
                        break;
                    }
                }
                if (!flag) {
                    return false;
                } else {
                    //만들 수 있는 경우 만들고 경사로 만든 인덱스 조정하기
                    cnt = 0;
                    i = i + L - 1;
                    prev = rows[i];
                }

            }

        }
        return true;
    }
}

문제 링크

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

 

풀이

청소기가 더 이상 움직일 수 없는 조건은 주변 칸이 전부 청소된 상태이고, 후진할 수 없는 경우이다.

while문을 실행하여 더 이상 움직일 수 없는 경우에 대해서 -1 값으로 처리했다.

move 함수는 로봇 청소기가 r, c, d값에 따라 한번 움직이는 함수이다. return값는 각 상황에 따른 새롭게 이동해야 하는 위치와 좌표가 반환된다.

move 함수에서 문제의 조건대로 함수를 실행한다.

 

개선할 점은 청소할 영역이 한가지만 남았는데, 로봇청소기가 해당 방향을 발견할때까지 뱅뱅 도는 경우이다

1 0 1
1 * 1
1 1 1

로봇 청소기가 *에 있을 때 0인 곳으로만 이동할 수 있는데 저 방향을 발견할때까지 계속 도는 경우가 있다.

어차피 청소하는 칸의 개수만 구하는 것이기 때문에 move 함수를 계속 실행시키는 것이 아니라 한 방향으로만 가야한다면 해당 방향을 바로 갈 수 있도록 return 값을 수정하는 것이 더 효율적인 코드라고 생각된다.

 

코드

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

public class Main_14503_로봇청소기 {

    static int N, M, res, map[][];
    static int[][] dir = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; //북 동 남 서

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokens = new StringTokenizer(br.readLine());

        N = Integer.parseInt(tokens.nextToken());
        M = Integer.parseInt(tokens.nextToken());
        map = new int[N][M];

        tokens = new StringTokenizer(br.readLine());
        //처음 위치와 방향
        int r = Integer.parseInt(tokens.nextToken());
        int c = Integer.parseInt(tokens.nextToken());
        int d = Integer.parseInt(tokens.nextToken());

        for (int i = 0; i < N; i++) {
            tokens = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                //0 청소되지 않은 칸, 1 벽
                map[i][j] = Integer.parseInt(tokens.nextToken());
            }
        }

        res = 0;

        while (r != -1 && c != -1 && d != -1) {
            int[] temp = move(r, c, d);
            r = temp[0];
            c = temp[1];
            d = temp[2];
        }

        System.out.println(res);
    }

    private static int[] move(int r, int c, int d) {
        //현재 칸 확인
        if (checkNowArea(r, c)) {
            //청소되지 않았다면 청소
            res++;
            map[r][c] = -1; //청소되었다고 표시
        }

        //주변 4칸 확인
        if (checkAroundArea(r, c)) {//다 청소된 경우
            //후진 가능한지 확인
            int[] temp = isBack(r, c, d);

            //후진 가능하다면 후진, 후진 불가능이면 작동 멈춤
            if (r == temp[0] && c == temp[1] && d == temp[2]) {
                //후진 불가능한 경우 멈춤
                return new int[]{-1, -1, -1};
            }
            //후진 가능한 경우
            return new int[]{temp[0], temp[1], temp[2]};

        } else {//청소되지 않은 칸이 있는 경우
            //방향 반시계로 90도 회전 -> 3 더하고 모듈러
            d = (d + 3) % 4;

            //앞쪽 칸이 청소되지 않은 빈칸이면 전진하고 다시 움직임
            int nx = r + dir[d][0];
            int ny = c + dir[d][1];
            if (isRange(nx, ny) && map[nx][ny] == 0) {//청소되지 않은 칸인 경우
                return new int[]{nx, ny, d}; //한칸 전진
            }
            //그냥 방향만 바꾸고 제자리
            return new int[]{r, c, d};

        }
    }

    private static int[] isBack(int r, int c, int d) {
        //현재 위치에서 후진 할 수 있는지 확인
        //현재 방향의 반대 방향 선택. 2를 더하고 모듈러
        int nd = (d + 2) % 4;
        int nx = r + dir[nd][0];
        int ny = c + dir[nd][1];

        //뒤로 갈 수 있고, 벽이 아닌 경우
        if (isRange(nx, ny) && map[nx][ny] != 1) return new int[]{nx, ny, d};
        return new int[]{r, c, d};

    }

    private static boolean checkAroundArea(int r, int c) {
        //주변에 0이 없다면 true
        for (int d = 0; d < 4; d++) {
            int nx = r + dir[d][0];
            int ny = c + dir[d][1];

            if (isRange(nx, ny) && map[nx][ny] == 0)
                return false;
        }
        return true;
    }

    private static boolean isRange(int x, int y) {
        if (x < 0 || y < 0 || x >= N || y >= M) return false;
        return true;
    }

    private static boolean checkNowArea(int r, int c) {
        if (map[r][c] == 0) return true; //청소되지 않은 칸이라면 true
        return false;
    }
}

 

문제 링크

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

 

풀이

감독관의 수가 int 자료형 범위를 벗어나기 때문에 long으로 선언

총감독관은 한 시험장마다 한명이기 때문에 응시자 수 - 감독가능수

부감독관은 남은 응시자 수 - 감독가능수를 했을 때 나머지가 0이라면 응시자수 / 감독가능수

나머지가 0이 아니라면 응시자 수 - 감독가능수 + 1감독관 수에 더해줌

 

코드

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

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine()); //시험장의 개수

        int[] map = new int[N];
        StringTokenizer tokens = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) { //응시자의 수
            map[i] = Integer.parseInt(tokens.nextToken());
        }

        tokens = new StringTokenizer(br.readLine());
        int B = Integer.parseInt(tokens.nextToken()); //총감독관
        int C = Integer.parseInt(tokens.nextToken()); //부감독관

        //총 감독관은 시험장 당 1명
        //부 감독관은 여러명 가능
        //감독관 최소

        long cnt = 0;
        for (int i = 0; i < N; i++) {
            //총감독관
            if (map[i] < B) {//총감독관이 모두 감독 가능
                cnt++;
                continue;
            }
            map[i] -= B;
            cnt++;

            //부감독관
            if (map[i] <= 0) continue;

            int a = map[i] / C;
            int b = map[i] % C;
            if (b != 0) {
                cnt += a + 1;
            } else {
                cnt += a;
            }
        }
        System.out.println(cnt);

    }
}

문제 링크

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

 

풀이

결과값이 될 그룹 단어의 개수를 N으로 초기화 한다.

입력 받은 단어의 글자수만큼 반복문을 실행한다.

반복문 안에서 단어를 한글자씩 읽고, map을 활용한다

1) getOrDefault를 사용하여 존재하는 경우 값을 가져오고, 없는 경우 -1로 처리한다.

2) -1인 경우에는 현재 단어가 몇번째 단어인지 인덱스를 같이 넣어준다

3) 값이 존재하는데, 그 값이 현재 단어의 인덱스와 1 차이가 아니라면 그룹 단어가 되지 못하기 때문에 그룹 단어의 수를 감소하고 다음 입력으로 넘어간다.

예를 들어 aba 를 읽었는데 map에서 'a'의 값은 0이다. 현재 인덱스는 2이기 때문에 둘의 차이가 1이 아니다. 따라서 그룹 단어가 되지 못한다.

4) 값이 존재하고, 인덱스 차이가 1이라면 현재 인덱스로 map의 값을 갱신한다.

 

코드

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

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        int res = N;
        for (int i = 0; i < N; i++) {
            HashMap<Character, Integer> map = new HashMap<>();
            String str = br.readLine();
            for (int j = 0; j < str.length(); j++) {
                char w = str.charAt(j);
                int val = map.getOrDefault(w, -1);
                if(val == -1){ //존재하지 않는 경우
                    map.put(w, j);
                    continue;
                }
                //값이 존재하는 경우 인덱스 비교
                if(j-1 != val){
                    //연속으로 존재하지 않는다 그룹단어가 아님
                    res--;
                    break;
                }
                //값이 존재하고 인덱스가 1개 차이 날 때
                map.replace(w, j);
            }
        }
        System.out.println(res);
    }
}

문제 링크

https://school.programmers.co.kr/learn/courses/30/lessons/144854

 

풀이

작가 아이디가 일치하고 경제인 행에 대해서만 조회

 

코드

select BOOK_ID, AUTHOR_NAME, date_format(PUBLISHED_DATE,"%Y-%m-%d") as PUBLISHED_DATE
from BOOK as b, AUTHOR as a
where CATEGORY = "경제" and b.AUTHOR_ID = a.AUTHOR_ID
order by PUBLISHED_DATE;

문제 링크

https://school.programmers.co.kr/learn/courses/30/lessons/133026

 

풀이

조인 실행 후 그룹함수를 적용한다.

그런데 문제 조건에 총주문량이 작은 순서대로 조회하는 SQL 문이라고 적혀있는데 해당 조건을 안 써도 통과가 된다.

그래도 정렬을 추가해서 다시 제출했다.

 

코드

select INGREDIENT_TYPE, SUM(TOTAL_ORDER) as TOTAL_ORDER
from FIRST_HALF as f join ICECREAM_INFO as i
on f.FLAVOR = i.FLAVOR
group by INGREDIENT_TYPE
order by TOTAL_ORDER;

문제 링크

https://school.programmers.co.kr/learn/courses/30/lessons/131530

 

풀이

그룹화 하여 그룹화 한 대상과 개수를 표시한다

TRUNCATE(대상, 자리수)는 대상을 자리수만큼 보일 수 있게 내림을 한다.

TRUNCATE(12.345, 2) 12.34
TRUNCATE(12.345, 0) 12
TRUNCATE(12.345, -2) 0

마이너스가 붙으면 0이 1의 자리가 되어 12가 나타나고, -2인 경우 3의 자리가 없이 때문에 0이 된다. -1을 하면 10이 나오게 됨

 

truncate를 PRICE_GROUP라고 지정했기 때문에 이것을 기준으로 그룹 함수를 사용한다.

 

코드

select truncate(price, -4) as PRICE_GROUP, count(product_id) as PRODUCTS
from product
group by PRICE_GROUP
order by PRICE_GROUP;

문제 링크

https://school.programmers.co.kr/learn/courses/30/lessons/131115

 

풀이

집계 함수 max를 사용하면 하나의 행만 리턴하기 때문에 최대 가격을 서브 쿼리로 가져오고 해당하는 가격의 상품 정보를 select 한다

 

코드

select product_id, product_name, product_cd, category, price
from food_product
where price = (
    select max(price)
    from food_product
);
1 2 3 4 5 ··· 7