문제

연종이는 엄청난 기억력을 가지고 있다. 그래서 하루 동안 본 정수들을 모두 기억 할 수 있다. 하지만 이를 믿을 수 없는 동규는 그의 기억력을 시험해 보기로 한다. 동규는 연종을 따라 다니며, 연종이 하루 동안 본 정수들을 모두 ‘수첩1’에 적어 놓았다. 그것을 바탕으로 그가 진짜 암기왕인지 알아보기 위해, 동규는 연종에게 M개의 질문을 던졌다. 질문의 내용은 “X라는 정수를 오늘 본 적이 있는가?” 이다. 연종은 막힘없이 모두 대답을 했고, 동규는 연종이 봤다고 주장하는 수 들을 ‘수첩2’에 적어 두었다. 집에 돌아온 동규는 답이 맞는지 확인하려 하지만, 연종을 따라다니느라 너무 힘들어서 여러분에게 도움을 요청했다. 동규를 도와주기 위해 ‘수첩2’에 적혀있는 순서대로, 각각의 수에 대하여, ‘수첩1’에 있으면 1을, 없으면 0을 출력하는 프로그램을 작성해보자.

입력

첫째 줄에 테스트케이스의 개수 T가 들어온다. 다음 줄에는 ‘수첩 1’에 적어 놓은 정수의 개수 N(1 ≤ N ≤ 1,000,000)이 입력으로 들어온다. 그 다음 줄에  ‘수첩 1’에 적혀 있는 정수들이 N개 들어온다. 그 다음 줄에는 ‘수첩 2’에 적어 놓은 정수의 개수 M(1 ≤ M ≤ 1,000,000) 이 주어지고, 다음 줄에 ‘수첩 2’에 적어 놓은 정수들이 입력으로 M개 들어온다. 모든 정수들의 범위는 int 로 한다.

출력

‘수첩2’에 적혀있는 M개의 숫자 순서대로, ‘수첩1’에 있으면 1을, 없으면 0을 출력한다.

시간 제한 : 2초

메모리 제한 :256 MB


문제의 조건 : N(1 ≤ N ≤ 1,000,000), M(1 ≤ M ≤ 1,000,000)

  • 배열을 정렬하여도 NM을 전부 비교하면 최악의 경우 1000000 * 1000000 번 비교를 하게 됩니다. O(N^2) ( 약 1조번의 반복으로 시간 제한에 걸리게 됩니다. )
  • 따라서 이분 탐색을 사용하여야 시간 제한 내에 성공할 수 있습니다. O(logN)

풀이 

이분 탐색을 사용하기 위해 우선 비교할 배열인 수첩 1을 정렬하였습니다.

// 이분 탐색을 위해 배열을 정렬해줍니다.
Arrays.sort(arr_1);

이후 수첩 2 배열을 순회하며 수첩 1 배열에 이분탐색을 하였습니다.

for (int i = 0; i < M; i++) {
	// bufferedWriter.append()는 결과를 출력에 담는 함수입니다. 
	// arr_2의 각 원소를 기준으로 arr_1을 이분탐색했습니다.
	bufferedWriter.append(binarySearch(arr_1, arr_2[i]) + "\\n");
}

이분탐색 코드

    // int[] target : 탐색할 배열, find : 찾을 수
    static int binarySearch(int[] target, int find) {

        // left , right
        int l = 0;
        int r = target.length - 1;

        while (l <= r) {
            // 중간 값을 비교
            int mid = (l+r)/2;

            // 같은 값을 찾은 경우 1을 리턴
            if (find == target[mid]) {
                return 1;
            }

            // 중간 값보다 찾는 수가 작은 경우
            if (find < target[mid]) {
                r = mid - 1;
            }
            else // if ( find > target[mid] ) 중간 값보다 찾는 수가 큰 경우
            {
                l = mid + 1;
            }
        }

        // 찾지 못한 경우
        return 0;
    }

전체 코드

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

/**
 * 암기왕
 */
public class BOJ2776 {

    static int T;
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
        T = Integer.parseInt(bufferedReader.readLine());

        while (T-- > 0) {
            int arr_1[], arr_2[];

            // input
            int N = Integer.parseInt(bufferedReader.readLine());
            String[] s = bufferedReader.readLine().split(" ");

            // 수첩 1 초기화 및 입력 값 저장
            arr_1 = new int[N];
            for (int i = 0; i < N; i++) {
                arr_1[i] = Integer.parseInt(s[i]);
            }

            // input
            int M = Integer.parseInt(bufferedReader.readLine());
            s = bufferedReader.readLine().split(" ");

            // 수첩 2 초기화 및 입력 값 저장
            arr_2 = new int[M];
            for (int i = 0; i < M; i++) {
                arr_2[i] = Integer.parseInt(s[i]);
            }

            // 이분 탐색을 위해 배열을 정렬해줍니다.
            Arrays.sort(arr_1);

            for (int i = 0; i < M; i++) {
                // bufferedWriter.append()는 결과를 출력에 담는 함수입니다.
                // arr_2의 각 원소를 기준으로 arr_1을 이분탐색했습니다.
                bufferedWriter.append(binarySearch(arr_1, arr_2[i]) + "\n");
            }

        }
        // 출력
        bufferedWriter.flush();

        // 종료
        bufferedReader.close();
        bufferedWriter.close();
    }

    // int[] target : 탐색할 배열, find : 찾을 수
    static int binarySearch(int[] target, int find) {

        // left , right
        int l = 0;
        int r = target.length - 1;

        while (l <= r) {
            // 중간 값을 비교
            int mid = (l+r)/2;

            // 같은 값을 찾은 경우 1을 리턴
            if (find == target[mid]) {
                return 1;
            }

            // 중간 값보다 찾는 수가 작은 경우
            if (find < target[mid]) {
                r = mid - 1;
            }
            else // if ( find > target[mid] ) 중간 값보다 찾는 수가 큰 경우
            {
                l = mid + 1;
            }
        }

        // 찾지 못한 경우
        return 0;
    }

}


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

 

2776번: 암기왕

연종이는 엄청난 기억력을 가지고 있다. 그래서 하루 동안 본 정수들을 모두 기억 할 수 있다. 하지만 이를 믿을 수 없는 동규는 그의 기억력을 시험해 보기로 한다. 동규는 연종을 따라 다니며,

www.acmicpc.net

 

'PS > 백준' 카테고리의 다른 글

[ BOJ 2776 ] 암기왕 ( JAVA )  (0) 2023.08.02
[ BOJ 3649 ] 로봇 프로젝트 ( JAVA )  (0) 2023.07.31
[ BOJ 1966 ] 프린터 큐 ( JAVA )  (0) 2023.07.26
[ BOJ 16234 ] 인구 이동 ( JAVA )  (0) 2023.07.24
[ BOJ 4179 ] 불! ( JAVA )  (0) 2023.05.26

문제

여러분도 알다시피 여러분의 프린터 기기는 여러분이 인쇄하고자 하는 문서를 인쇄 명령을 받은 ‘순서대로’, 즉 먼저 요청된 것을 먼저 인쇄한다. 여러 개의 문서가 쌓인다면 Queue 자료구조에 쌓여서 FIFO - First In First Out - 에 따라 인쇄가 되게 된다. 하지만 상근이는 새로운 프린터기 내부 소프트웨어를 개발하였는데, 이 프린터기는 다음과 같은 조건에 따라 인쇄를 하게 된다.

  1. 현재 Queue의 가장 앞에 있는 문서의 ‘중요도’를 확인한다.
  2. 나머지 문서들 중 현재 문서보다 중요도가 높은 문서가 하나라도 있다면, 이 문서를 인쇄하지 않고 Queue의 가장 뒤에 재배치 한다. 그렇지 않다면 바로 인쇄를 한다.

예를 들어 Queue에 4개의 문서(A B C D)가 있고, 중요도가 2 1 4 3 라면 C를 인쇄하고, 다음으로 D를 인쇄하고 A, B를 인쇄하게 된다.

여러분이 할 일은, 현재 Queue에 있는 문서의 수와 중요도가 주어졌을 때, 어떤 한 문서가 몇 번째로 인쇄되는지 알아내는 것이다. 예를 들어 위의 예에서 C문서는 1번째로, A문서는 3번째로 인쇄되게 된다.

입력

첫 줄에 테스트케이스의 수가 주어진다. 각 테스트케이스는 두 줄로 이루어져 있다.

테스트케이스의 첫 번째 줄에는 문서의 개수 N(1 ≤ N ≤ 100)과, 몇 번째로 인쇄되었는지 궁금한 문서가 현재 Queue에서 몇 번째에 놓여 있는지를 나타내는 정수 M(0 ≤ M < N)이 주어진다. 이때 맨 왼쪽은 0번째라고 하자. 두 번째 줄에는 N개 문서의 중요도가 차례대로 주어진다. 중요도는 1 이상 9 이하의 정수이고, 중요도가 같은 문서가 여러 개 있을 수도 있다.

출력

각 테스트 케이스에 대해 문서가 몇 번째로 인쇄되는지 출력한다.

 

시간 제한 : 2초

메모리 제한 : 128MB


주어진 조건인 N 값과 중요도 값에 비해 시간 제한메모리 제한이 넉넉하며, 문제의 조건에 맞게 구현하는 문제입니다.
우선 문서에 대한 내부 클래스를 생성하여 문서의 초기 번호, 중요도를 저장하였고, Queue를 이용하여 문제에 1, 2번 조건에 따라 Queue에서 하나씩 꺼내면서 더 큰 중요도가 있는지 확인하는 방식으로 구현하였습니다. 

static class Info{
    int num;    // 초기 순서
    int p;      // 중요도

    public Info(int num, int p) {
        this.num = num;
        this.p = p;
    }
}
  • 문서를 나타내는 내부 클래스입니다. 입력 시점초기 순서중요도를 저장하여 Queue에 삽입합니다.
// Queue 초기화 및 중요도 수 count
for (int i = 0; i < N; i++) {
    int p = Integer.parseInt(input[i]);
    // i: 초기 Queue 순서, p: 중요도 
    queue.add(new Info(i, p));
    cnt[p]++;
}
  • 입력값을 반영하여 Queue에 문서를 넣습니다. 
  • 중요도의 수Cnt 배열에 저장하여 현재 꺼낸 문서보다 큰 중요도가 존재하는지 파악할때 사용합니다.
    ex ) cnt[3] : 중요도가 3인 문서의 수
while (!queue.isEmpty()) {
    Info now = queue.poll();
    boolean isFirst = true;

    // 가장 앞에 있는 문서(now)의 중요도보다 높은 문서가 있는지 확인
    for (int i = now.p + 1; i < 10; i++) {
        // 높은 문서가 존재하는 경우 
        if (cnt[i] > 0) {
            isFirst = false;
            break;
        }
    }

    // 위에서 확인한 결과로 만약 중요도가 높은 문서가 있으면 Queue에 마지막에 재배치
    if (!isFirst) {
        queue.add(now);
        continue;
    }

    // 현재 문서보다 높은 중요도가 없는 경우 꺼내는 순서와 count값 조정
    result++;
    cnt[now.p]--;
    
    // 현재 꺼낸 문서가 찾던 문서이면 반복 종료
    if(M == now.num) break;
}
  • Queue에서 꺼낸 문서의 중요도를 사용하여 cnt[]탐색하고 현재 꺼낸 문서보다 중요도가 존재하는 경우 isFirst = false가 됩니다.
  • 이후 다음 줄에서 isFirst를 검사하여 현재 문서보다 중요도가 존재하는 경우 Queue에 다시 문서를 넣습니다.
  • 현재 꺼낸 문서의 중요도가 가장 큰 경우는 문서를 꺼내고 이를 result ( 꺼낸 순서 ) , cnt[] 에 반영합니다. 
  • 현재 꺼낸 문서의 초기 순서가 찾던 문서 ( M ) 이면 반복을 종료합니다.

 

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

 

1966번: 프린터 큐

여러분도 알다시피 여러분의 프린터 기기는 여러분이 인쇄하고자 하는 문서를 인쇄 명령을 받은 ‘순서대로’, 즉 먼저 요청된 것을 먼저 인쇄한다. 여러 개의 문서가 쌓인다면 Queue 자료구조에

www.acmicpc.net

'PS > 백준' 카테고리의 다른 글

[ BOJ 3649 ] 로봇 프로젝트 ( JAVA )  (0) 2023.07.31
[ BOJ 2776 ] 암기왕 ( JAVA )  (0) 2023.07.28
[ BOJ 16234 ] 인구 이동 ( JAVA )  (0) 2023.07.24
[ BOJ 4179 ] 불! ( JAVA )  (0) 2023.05.26
[ BOJ 2075 ] N번째 큰 수 ( JAVA )  (1) 2023.05.16

문제

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모든 나라는 1×1 크기이기 때문에, 모든 국경선은 정사각형 형태이다.

오늘부터 인구 이동이 시작되는 날이다.

인구 이동은 하루 동안 다음과 같이 진행되고, 더 이상 아래 방법에 의해 인구 이동이 없을 때까지 지속된다.

국경선을 공유하는 두 나라의 인구 차이가 L명 이상, R명 이하라면, 두 나라가 공유하는 국경선을 오늘 하루 동안 연다.
위의 조건에 의해 열어야하는 국경선이 모두 열렸다면, 인구 이동을 시작한다.
국경선이 열려있어 인접한 칸만을 이용해 이동할 수 있으면, 그 나라를 오늘 하루 동안은 연합이라고 한다.
연합을 이루고 있는 각 칸의 인구수는 (연합의 인구수) / (연합을 이루고 있는 칸의 개수)가 된다. 편의상 소수점은 버린다.
연합을 해체하고, 모든 국경선을 닫는다.
각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 프로그램을 작성하시오.

시간 제한 : 2초
메모리 제한 : 512MB


연합을 이루기 위해 각 나라들에서 상하좌우로 인접한 나라들과의 인구 차이를 비교할 필요가 있습니다.

이때 특정 나라가 속한 연합을 찾기 위해서 그 나라에서 DFS를 통해 인구 차이L명 이상, R명 이하인 나라로 탐색하며 연합을 구했습니다.
연합을 구한 이후 속한 나라들에 연산을 적용하는 방법으로 한 연합에 대한 인구 이동을 끝냈고, 방문하지 않은 모든 나라에 동일한 연산을 적용하였습니다.

연합끼리는 영향을 주지 않기 때문에 원본 map 배열에 바로 값을 업데이트하였습니다. 

public static boolean move() {
    boolean visit[][] = new boolean[map.length][map[0].length];
    isMoved = false;

    // 모든 나라에 대해 수행
    for (int i = 0; i < map.length; i++) {
        for (int j = 0; j < map[0].length; j++) {
            if (!visit[i][j]) {
                dfs(i, j, visit);
            }
        }
    }
    return isMoved;
}

boolean 타입의 visit[][] 배열을 선언하여, 방문하지 않은 모든 나라에 대해 dfs를 시작합니다. 

public static boolean dfs(int i, int j, boolean[][] visit) {
    Stack<Country> stack = new Stack<>();
    List<Country> tmp = new ArrayList<>();
    int groupSum = 0;

    // 처음 방문하는 나라를 stack에 추가
    stack.add(new Country(i, j));
    visit[i][j] = true;

    while (!stack.isEmpty()) {
        Country now = stack.pop();
        tmp.add(now);

        groupSum += map[now.x][now.y];

        for (int m = 0; m < 4; m++) {
            int xx = now.x + mx[m];
            int yy = now.y + my[m];

            if (xx >= 0 && xx < map.length && yy >= 0 && yy < map[0].length && !visit[xx][yy]) {
                if (check(map[now.x][now.y], map[xx][yy])) {
                    visit[xx][yy] = true;
                    isMoved = true;
                    stack.add(new Country(xx, yy));
                }
            }
        }
    }

    if(tmp.size() > 0)
        groupSum /= tmp.size();

    for (Country c : tmp) map[c.x][c.y] = groupSum;

    return isMoved;
}

stack을 사용하여 dfs를 수행하는 부분입니다.
인구 이동이 가능하면서 방문하지 않은 나라를 다음 탐색 나라로 추가합니다. (

이후 인구 이동 발생 여부를 확인하기 위해 isMoved라는 값을 리턴합니다.

public static boolean check(int n1, int n2){

    if (n1 < n2) {
        int tmp = n1;
        n1 = n2;
        n2 = tmp;
    }

    if( ((n1-n2) >= L) && ((n1-n2) <= R) ) return true;

    return false;
}

인구 이동이 가능한지 확인하는 메서드입니다. 

기본적으로 n1이 더 크다고 가정하였기 때문에 n1이 더 작은 경우 swap을 통해 차를 계산합니다.

 

 

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

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

'PS > 백준' 카테고리의 다른 글

[ BOJ 2776 ] 암기왕 ( JAVA )  (0) 2023.07.28
[ BOJ 1966 ] 프린터 큐 ( JAVA )  (0) 2023.07.26
[ BOJ 4179 ] 불! ( JAVA )  (0) 2023.05.26
[ BOJ 2075 ] N번째 큰 수 ( JAVA )  (1) 2023.05.16
[ BOJ 9205 ] 맥주 마시면서 걸어가기 ( JAVA )  (0) 2023.04.11

불!


문제

지훈이는 미로에서 일을 한다. 지훈이를 미로에서 탈출하도록 도와주자!

미로에서의 지훈이의 위치와 불이 붙은 위치를 감안해서 지훈이가 불에 타기전에 탈출할 수 있는지의 여부, 그리고 얼마나 빨리 탈출할 수 있는지를 결정해야한다.

지훈이와 불은 매 분마다 한칸씩 수평또는 수직으로(비스듬하게 이동하지 않는다) 이동한다.

불은 각 지점에서 네 방향으로 확산된다.

지훈이는 미로의 가장자리에 접한 공간에서 탈출할 수 있다.

지훈이와 불은 벽이 있는 공간은 통과하지 못한다.

입력

입력의 첫째 줄에는 공백으로 구분된 두 정수 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1000 이다. R은 미로 행의 개수, C는 열의 개수이다.

다음 입력으로 R줄동안 각각의 미로 행이 주어진다.

각각의 문자들은 다음을 뜻한다.

  • #: 벽
  • .: 지나갈 수 있는 공간
  • J: 지훈이의 미로에서의 초기위치 (지나갈 수 있는 공간)
  • F: 불이 난 공간

J는 입력에서 하나만 주어진다.

출력

지훈이가 불이 도달하기 전에 미로를 탈출 할 수 없는 경우 IMPOSSIBLE 을 출력한다.

지훈이가 미로를 탈출할 수 있는 경우에는 가장 빠른 탈출시간을 출력한다.

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
1 초 256 MB 39000 8622 5840 20.875%

구현

- 이 문제는 지훈이와 불을 동시에 움직여야 하는 문제입니다. 

 

지훈이는 오직 '.'으로만 움직일 수 있습니다. ( 불이 있는 칸이나 벽으로 이동할 수 없음)

그러나 불의 경우는 벽을 제외하면 지훈이가 있는 곳으로도 번질 수 있기 때문에, 지훈이를 먼저 이동시킨 후 불을 이동시켰습니다!

 

지훈이가 이동할 때 만약 배열 밖으로 이동할 수 있다면 탈출 처리를 하였고, 밖으로 나가지 못하고 모든 탐색이 끝나면 탈출하지 못한 것으로 처리하였습니다.

 

( 처음에는 불의 시작지점도 1개만 있다고 가정하고 풀었는데 틀렸고, 불의 시작지점을 여러개로 가정한 후 AC를 받았습니다 ㅠ)     

 

제출 결과


소스코드

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

/**
 * 불!
 */
public class BOJ4179 {

    static int mx[] = {0, -1, 0, 1};
    static int my[] = {-1, 0, 1, 0};

    static int r, c;
    static char map[][];
    static int visit[][];
    static int INF = 150000; // R,C의 최댓값이 1000이므로 1000 * 1000 보다 크게 넉넉잡아 설정했습니다

    static List<Info> fire = new ArrayList<>(); // 불 시작지점
    static Info J; // 지훈 시작지점
    
    public static void main(String[] args) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String[] s = bufferedReader.readLine().split(" ");

        r = Integer.parseInt(s[0]);
        c = Integer.parseInt(s[1]);

        map = new char[r][c];
        visit = new int[r][c];

        for (int i = 0; i < r; i++) {
            Arrays.fill(visit[i], INF);
            String input = bufferedReader.readLine();
            
            for (int j = 0; j < c; j++) {
                map[i][j] = input.charAt(j);

				// 불의 시작지점을 리스트에 추가
                if (map[i][j] == 'F') {
                    fire.add(new Info(i, j, true));
                }

                if (map[i][j] == 'J') {
                    J = new Info(i, j, false);
                    visit[i][j] = 0;
                }
            }
        }

        int result = escape();

        System.out.println((result==-1) ? "IMPOSSIBLE": result);
    }

    static int escape() {
        Queue<Info> queue = new LinkedList<>();
			
        // 지훈이부터 움직이도록 먼저 큐에 넣습니다.
        queue.add(J);
		
        for (Info info : fire) {
            queue.add(info);
        }

        while (!queue.isEmpty()) {
            Info cur = queue.poll();

            // 지훈이 이동
            if (!cur.isFire) {
				
                if(map[cur.x][cur.y] == 'F') continue;

                for (int i = 0; i < 4; i++) {
                    int xx = cur.x + mx[i];
                    int yy = cur.y + my[i];

                    // 탈출성공
                    if (xx < 0 || xx >= r || yy < 0 || yy >= c)
                    {
                        return visit[cur.x][cur.y] + 1;
                    }

                    // 이동 가능한 공간이면서 처음 가는 곳이면 다음 칸으로 이동
                    if (xx >= 0 && xx < r && yy >= 0 && yy < c && map[xx][yy] == '.' && visit[xx][yy] == INF)
                    {
                        visit[xx][yy] = visit[cur.x][cur.y] + 1;
                        map[xx][yy] = 'J';
                        queue.add(new Info(xx, yy, false));
                    }

                }
            }
            
            // 불 이동
            else {
                for (int i = 0; i < 4; i++) {
                    int xx = cur.x + mx[i];
                    int yy = cur.y + my[i];

                    // 얘는 벽이나 불 아니면 한칸씩 번집니다.
                    if (xx >= 0 && xx < r && yy >= 0 && yy < c && map[xx][yy] != '#' && map[xx][yy] != 'F')
                    {
                        map[xx][yy] = 'F';
                        queue.add(new Info(xx, yy, true));
                    }
                }
            }
        }

        return -1;
    }

    static class Info {

        int x, y;
        boolean isFire;

        public Info(int x, int y, boolean isFire) {
            this.x = x;
            this.y = y;
            this.isFire = isFire;
        }
    }

}

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

 

4179번: 불!

입력의 첫째 줄에는 공백으로 구분된 두 정수 R과 C가 주어진다. 단, 1 ≤ R, C ≤ 1000 이다. R은 미로 행의 개수, C는 열의 개수이다. 다음 입력으로 R줄동안 각각의 미로 행이 주어진다. 각각의 문자

www.acmicpc.net


 

 

+ Recent posts