문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 (추가 시간 없음) 512 MB 39509 18717 12827 46.147%

문제

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의 번호이고, 행과 열의 번호는 1부터 시작한다. 각각의 칸은 빈 칸이거나 벽이다.

오늘은 집 수리를 위해서 파이프 하나를 옮기려고 한다. 파이프는 아래와 같은 형태이고, 2개의 연속된 칸을 차지하는 크기이다.

파이프는 회전시킬 수 있으며, 아래와 같이 3가지 방향이 가능하다.

 

파이프는 매우 무겁기 때문에, 유현이는 파이프를 밀어서 이동시키려고 한다. 벽에는 새로운 벽지를 발랐기 때문에, 파이프가 벽을 긁으면 안 된다. 즉, 파이프는 항상 빈 칸만 차지해야 한다.

파이프를 밀 수 있는 방향은 총 3가지가 있으며, →, ↘, ↓ 방향이다. 파이프는 밀면서 회전시킬 수 있다. 회전은 45도만 회전시킬 수 있으며, 미는 방향은 오른쪽, 아래, 또는 오른쪽 아래 대각선 방향이어야 한다.

파이프가 가로로 놓여진 경우에 가능한 이동 방법은 총 2가지, 세로로 놓여진 경우에는 2가지, 대각선 방향으로 놓여진 경우에는 3가지가 있다.

아래 그림은 파이프가 놓여진 방향에 따라서 이동할 수 있는 방법을 모두 나타낸 것이고, 꼭 빈 칸이어야 하는 곳은 색으로 표시되어져 있다.

 

대각선

가장 처음에 파이프는 (1, 1)와 (1, 2)를 차지하고 있고, 방향은 가로이다. 파이프의 한쪽 끝을 (N, N)로 이동시키는 방법의 개수를 구해보자.

입력

첫째 줄에 집의 크기 N(3 ≤ N ≤ 16)이 주어진다. 둘째 줄부터 N개의 줄에는 집의 상태가 주어진다. 빈 칸은 0, 벽은 1로 주어진다. (1, 1)과 (1, 2)는 항상 빈 칸이다.

출력

첫째 줄에 파이프의 한쪽 끝을 (N, N)으로 이동시키는 방법의 수를 출력한다. 이동시킬 수 없는 경우에는 0을 출력한다. 방법의 수는 항상 1,000,000보다 작거나 같다.

풀이

DFS를 활용하여 N,N으로 도달하는 경우의 수를 구했다.

문제 설명에서 보이듯이 오른쪽, 중간, 아래로 갈 수 있는 경우가 있고, 이전에 어떤 경로로 왔는지에 따라 갈 수 있는 방향이 달라진다. 따라서 아래와 같이 정리 후 dfs를 수행하도록 했다.

  1. 오른쪽으로 이동 했을 경우
    1. 오른쪽 이동 (오른쪽에 벽이 있을 시 이동 불가)
    2. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
  2. 중간으로 이동 했을 경우
    1. 오른쪽 이동 (오른쪽에 벽이 있을 시 이동 불가)
    2. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
    3. 아래쪽 이동 (아래에 벽이 있을 시 이동 불가)
  3. 아래쪽으로 이동 했을 경우
    1. 중간 이동 (오른쪽, 중간, 아래에 벽이 있을 시 이동 불가)
    2. 아래쪽 이동 (아래에 벽이 있을 시 이동 불가)

또한, N,N에 도달했을 경우, 더이상 오른쪽으로 이동 할 수 없는 경우, 아래쪽으로 이동할 수 없는 경우에 대해 처리해줬다.

코드

package com.company.baekjoon;

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

public class B17070 {
    static int N;
    static int[][] arr;
    static int count = 0;

    static int TO_RIGHT = 1;
    static int TO_MIDDLE = 2;
    static int TO_BOTTOM = 3;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        arr = new int[N][N];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }
        int currentY = 0;
        int currentX = 1;
        dfs(currentY, currentX, TO_RIGHT);

        System.out.println(count);
    }
    public static void dfs(int cy, int cx, int type) {
        if (cy == N - 1 && cx == N - 1) {
            count++;
            return;
        } else if (type == TO_RIGHT && cx == N - 1)
            return;
        else if (type == TO_BOTTOM && cy == N - 1)
            return;

        if (type == TO_RIGHT) {
            if (cx + 1 < N && arr[cy][cx+1] != 1) {
                dfs(cy, cx+1, TO_RIGHT);
            }
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
        } else if (type == TO_MIDDLE) {
            if (cx + 1 < N && arr[cy][cx+1] != 1) {
                dfs(cy, cx+1, TO_RIGHT);
            }
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
            if (cy + 1 < N && arr[cy+1][cx] != 1) {
                dfs(cy+1, cx, TO_BOTTOM);
            }
        } else if (type == TO_BOTTOM) {
            if (cx + 1 < N && cy + 1 < N && arr[cy+1][cx+1] != 1 && arr[cy+1][cx] != 1 && arr[cy][cx+1] != 1) {
                dfs(cy+1,cx+1, TO_MIDDLE);
            }
            if (cy + 1 < N && arr[cy+1][cx] != 1) {
                dfs(cy+1, cx, TO_BOTTOM);
            }
        }
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 512 MB 61274 33242 22550 53.653%

문제

로봇 청소기와 방의 상태가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오.

로봇 청소기가 있는 방은 NxM 크기의 직사각형으로 나타낼 수 있으며, 1x1 크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 벽 또는 빈 칸이다. 청소기는 바라보는 방향이 있으며, 이 방향은 동, 서, 남, 북 중 하나이다. 방의 각 칸은 좌표 (r, c)로 나타낼 수 있고, 가장 북쪽 줄의 가장 서쪽 칸의 좌표가 (0, 0) 가장 남쪽 줄의 가장 동쪽 칸의 좌표가 (N-1, M-1)이다. 즉, 좌표(r, c)는 북쪽에서 (r+1) 번째에 있는 줄의 서쪽에서 (c+1) 번째 칸을 가리킨다. 처음에 빈 칸은 전부 청소되지 않은 상태이다.

로봇 청소기는 다음과 같이 작동한다.

  1. 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
  2. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우,
    1. 바라보는 방향을 유지한 채로 한 칸 후진할 수 있다면 한 칸 후진하고 1번으로 돌아간다.
    2. 바라보는 방향의 뒤쪽 칸이 벽이라 후진할 수 없다면 작동을 멈춘다.
  3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우,
    1. 반시계 방향으로 90∘ 회전한다.
    2. 바라보는 방향을 기준으로 앞쪽 칸이 청소되지 않은 빈 칸인 경우 한 칸 전진한다.
    3. 1번으로 돌아간다.

입력

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

셋째 줄부터 N개의 줄에 각 장소의 상태를 나타내는 NxM개의 값이 한 줄에 M개씩 입력된다. i번째 줄의 j번째 값은 칸 (i, j)의 상태를 나타내며, 이 값이 0인 경우 (i, j)가 청소되지 않은 빈 칸이고, 1인 경우 (i, j)에 벽이 있는 것이다. 방의 가장 북쪽, 가장 남쪽, 가장 서쪽, 가장 동쪽 줄 중 하나 이상에 위치한 모든 칸에는 벽이 있다. 로봇 청소기가 있는 칸은 항상 빈 칸이다.

출력

로봇 청소기가 작동을 시작한 후 작동을 멈출 때까지 청소하는 칸의 개수를 출력한다.

풀이

구현 문제이며, 문제만 잘 읽는다면 어려움 없이 풀이할 수 있다.

코드

package com.company.baekjoon;

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

public class B14503 {

    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,1,-1};

    static int NOT_CLEANED = 0;
    static int WALL = 1;
    static int CLEANED = 2;
    static int n;
    static int m;
    static int[][] arr;

    static int r;
    static int c;
    static int d;
    static int count;
    static boolean state = true;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        arr = new int[n][m];

        st = new StringTokenizer(br.readLine());

        r = Integer.parseInt(st.nextToken());
        c = Integer.parseInt(st.nextToken());
        d = Integer.parseInt(st.nextToken());

        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                int val = Integer.parseInt(st.nextToken());
                arr[i][j] = val;
            }
        }
        while (state) {
            // 1. 현재 칸이 아직 청소되지 않은 경우, 현재 칸을 청소한다.
            cleanCurrent();

            if (checkCleaned() == 4) { // 2.현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 없는 경우
                checkBack();
            }
            else if (checkNotCleaned() > 0) { // 3. 현재 칸의 주변 4칸 중 청소되지 않은 빈 칸이 있는 경우
                rotate();
                goToForward();
            }
        }
        System.out.println(count);

    }
    public static void cleanCurrent() {
        if (arr[r][c] == NOT_CLEANED) {
            arr[r][c] = CLEANED;
            count++;
        }
    }
    public static void rotate() {
        if (d == 0) {
            d = 3;
        } else if (d == 1) {
            d = 0;
        } else if (d == 2) {
            d = 1;
        } else if (d == 3) {
            d = 2;
        }
    }
    public static void checkBack() {
        if (d == 0) { // 북
            if (arr[r+1][c] == WALL) {
                state = false;
            } else {
                r = r + 1;
            }
        } else if (d == 1) { // 동
            if (arr[r][c-1] == WALL) {
                state = false;
            } else {
                c = c - 1;
            }
        } else if (d == 2) { // 남
            if (arr[r-1][c] == WALL) {
                state = false;
            } else {
                r = r - 1;
            }
        } else if (d == 3) { // 서
            if (arr[r][c+1] == WALL) {
                state = false;
            } else {
                c = c + 1;
            }
        }
    }
    public static int checkNotCleaned() {
        int notCleanedCount = 0;
        if (arr[r+1][c] != CLEANED) {
            notCleanedCount++;
        }
        if (arr[r-1][c] != CLEANED) {
            notCleanedCount++;
        }
        if (arr[r][c+1] != CLEANED) {
            notCleanedCount++;
        }
        if (arr[r][c-1] != CLEANED) {
            notCleanedCount++;
        }
        return notCleanedCount;
    }
    public static void goToForward() {
        if (d == 0) { // 북
            if (arr[r-1][c] == NOT_CLEANED) {
                r = r - 1;
            }
        } else if (d == 1) { // 동
            if (arr[r][c+1] == NOT_CLEANED) {
                c = c + 1;
            }
        } else if (d == 2) { // 남
            if (arr[r+1][c] == NOT_CLEANED) {
                r = r + 1;
            }
        } else if (d == 3) { // 서
            if (arr[r][c-1] == NOT_CLEANED) {
                c = c - 1;
            }
        }
    }
    public static int checkCleaned() {
        int cleanedCount = 0;
        if (arr[r+1][c] != NOT_CLEANED) {
            cleanedCount++;
        }
        if (arr[r-1][c] != NOT_CLEANED) {
            cleanedCount++;
        }
        if (arr[r][c-1] != NOT_CLEANED) {
            cleanedCount++;
        }
        if (arr[r][c+1] != NOT_CLEANED) {
            cleanedCount++;
        }
        return cleanedCount;
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

1 초 256 MB 166553 64421 47778 36.624%

문제

2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오.

아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.

입력

첫째 줄에 n이 주어진다. (1 ≤ n ≤ 1,000)

출력

첫째 줄에 2×n 크기의 직사각형을 채우는 방법의 수를 10,007로 나눈 나머지를 출력한다.

풀이

아래처럼 실제로 직사각형을 채우는 방법을 나열 해본 후 n번째 직사각형을 채우는 방법은 n-1번째와 n-2번째의 합이라는 것을 알 수 있었다.

이 후 방법의 수를 10,007로 나누어 출력해줬다.

1 - 1
1

2 - 2
1 1
2 2

3 - 3
1 1 1
1 2 2
2 2 1

4 - 5
1 1 1 1
1 2 2 1
2 2 1 1
1 1 2 2
2 2 2 2

5 - 8  
1 1 1 1 1
1 2 2 1 1
2 2 1 1 1
1 1 2 2 1
2 2 2 2 1
1 1 1 2 2
1 2 2 2 2
2 2 1 2 2

 

코드

package com.company.baekjoon;

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

public class B11726 {
	static long[] dp = new long[1001];
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int n = Integer.parseInt(st.nextToken());

		dp[1] = 1;
		dp[2] = 2;
		dp[3] = 3;
		for (int i = 4; i <= n; i++) {
			dp[i] = (dp[i-1] % 10007) + (dp[i-2] % 10007);
		}

		System.out.println(dp[n] % 10007);
	}
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 42619 16415 12036 37.043%

문제

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자. 물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다. 예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고, 동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.

도시들의 개수와 도시들 간의 연결 여부가 주어져 있고, 동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때 가능한지 여부를 판별하는 프로그램을 작성하시오. 같은 도시를 여러 번 방문하는 것도 가능하다.

입력

첫 줄에 도시의 수 N이 주어진다. N은 200이하이다. 둘째 줄에 여행 계획에 속한 도시들의 수 M이 주어진다. M은 1000이하이다. 다음 N개의 줄에는 N개의 정수가 주어진다. i번째 줄의 j번째 수는 i번 도시와 j번 도시의 연결 정보를 의미한다. 1이면 연결된 것이고 0이면 연결이 되지 않은 것이다. A와 B가 연결되었으면 B와 A도 연결되어 있다. 마지막 줄에는 여행 계획이 주어진다. 도시의 번호는 1부터 N까지 차례대로 매겨져 있다.

출력

첫 줄에 가능하면 YES 불가능하면 NO를 출력한다.

풀이

예제의 여행 계획을 보면 각 노드들이 모두 연결되어 있기만 하면 어떻게든 경유하여 목적을 달성할 수 있다. 따라서, dfs 또는 bfs를 이용하여 연결된 노드를 모두 탐색한다. 나의 경우 dfs를 통해 풀었고, 검색 기준은 여행계획의 첫번째 도시로 설정했다.

코드

package com.company.baekjoon;

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

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

		int N = Integer.parseInt(st.nextToken());
		st = new StringTokenizer(br.readLine());

		int [][] graph = new int[N][N];
		boolean[] visited = new boolean[N];
		boolean[] target = new boolean[N];
		int M = Integer.parseInt(st.nextToken());

		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());

			for (int j = 0; j < N; j++) {
				graph[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		st = new StringTokenizer(br.readLine());

		int start = 0;
		boolean checkStart = false;
		while (st.hasMoreTokens()) {
			int val = Integer.parseInt(st.nextToken()) - 1;
			if (!checkStart) {
				start = val;
				checkStart = true;
			}
			target[val] = true;
		}

		dfs(graph,visited, start);
		boolean isTravel = checkCity(visited, target);

		if (isTravel) {
			System.out.println("YES");
		} else {
			System.out.println("NO");
		}
	}
	public static boolean checkCity(boolean[] visited, boolean[] target) {
		boolean isTravel = false;
		for (int i = 0; i <visited.length; i++) {
			if (target[i]) { // 꼭 들러야 하는곳이라면
				if (visited[i]) {
					isTravel = true;
				} else {
					isTravel = false;
					break;
				}
			}
		}
		return isTravel;
	}
	public static void dfs(int[][] graph, boolean[] visited, int start) {
		visited[start] = true;
		for (int i = 0; i < graph.length; i++) {
			if (!visited[i] && graph[start][i] == 1) {
				dfs(graph, visited, i);
			}
		}
	}
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율

2 초 128 MB 39301 13232 10581 32.815%

문제

길이가 N인 수열이 주어졌을 때, 그 수열의 합을 구하려고 한다. 하지만, 그냥 그 수열의 합을 모두 더해서 구하는 것이 아니라, 수열의 두 수를 묶으려고 한다. 어떤 수를 묶으려고 할 때, 위치에 상관없이 묶을 수 있다. 하지만, 같은 위치에 있는 수(자기 자신)를 묶는 것은 불가능하다. 그리고 어떤 수를 묶게 되면, 수열의 합을 구할 때 묶은 수는 서로 곱한 후에 더한다.

예를 들면, 어떤 수열이 {0, 1, 2, 4, 3, 5}일 때, 그냥 이 수열의 합을 구하면 0+1+2+4+3+5 = 15이다. 하지만, 2와 3을 묶고, 4와 5를 묶게 되면, 0+1+(23)+(45) = 27이 되어 최대가 된다.

수열의 모든 수는 단 한번만 묶거나, 아니면 묶지 않아야한다.

수열이 주어졌을 때, 수열의 각 수를 적절히 묶었을 때, 그 합이 최대가 되게 하는 프로그램을 작성하시오.

입력

첫째 줄에 수열의 크기 N이 주어진다. N은 50보다 작은 자연수이다. 둘째 줄부터 N개의 줄에 수열의 각 수가 주어진다. 수열의 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

출력

수를 합이 최대가 나오게 묶었을 때 합을 출력한다. 정답은 항상 2(31)보다 작다.

풀이

먼저 정답은 항상 2의 31제곱보다 작으므로 int형으로 저장한다.

또한 문제를 세가지 경우로 나눠서 큐에 담았고 각각 처리를 해줬다.

  1. 양수인 경우
    1. 1개만 꺼낼 수 있는 경우 덧셈 해준다.
    2. 2개 꺼낼 수 있고, 꺼낸 수가 2개 다 1이 아닌 양수인 경우 무조건 곱셈을 하는것이 높은 수를 얻을 수 있다.
    3. 2개 꺼낼 수 있고, 꺼낸 수에 1개 이상 1이 있는경우 덧셈을 하는것이 좋다.
  2. 음수인 경우
    1. 2개 꺼낼 수 있고, 꺼낸 수가 2개 다 음수인 경우 무조건 곱셈을 하는것이 높은 수를 얻을 수 있다.
    2. 1개 꺼낼 수 있고, 0이 든 큐에 데이터가 있을 경우 곱셈하여 음수를 없애준다.
    3. 1개 꺼낼 수있고, 0이 든 큐에 데이터가 없을 경우 음수를 더해준다.

코드

package com.company.baekjoon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;

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

		int N = Integer.parseInt(st.nextToken());

		PriorityQueue<Integer> q = new PriorityQueue<>(Comparator.reverseOrder());
		PriorityQueue<Integer> rq = new PriorityQueue<>();
		Queue<Integer> zeroq = new LinkedList<>();
		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());

			int value = Integer.parseInt(st.nextToken());
			if (value > 0) {
				q.add(value);
			} else if (value == 0) {
				zeroq.add(value);
			} else {
				rq.add(value);
			}

		}
		int answer = 0;
		while (!q.isEmpty()) {
			int first = q.poll();
			if (q.isEmpty()) {
				answer += first;
				break;
			}
			int second = q.poll();
			if (first == 1 || second == 1)
				answer += first + second;
			else
				answer += first * second;
		}

		while (!rq.isEmpty()) {
			int first = rq.poll();
			if (rq.isEmpty()) {
				if (!zeroq.isEmpty()) {
					zeroq.poll();
				} else {
					answer += first;
				}
				break;
			}
			int second = rq.poll();

			answer += first * second;
		}
		System.out.println(answer);
	}
}

문제

문제 설명

계속되는 폭우로 일부 지역이 물에 잠겼습니다. 물에 잠기지 않은 지역을 통해 학교를 가려고 합니다. 집에서 학교까지 가는 길은 m x n 크기의 격자모양으로 나타낼 수 있습니다.

아래 그림은 m = 4, n = 3 인 경우입니다.

!https://grepp-programmers.s3.amazonaws.com/files/ybm/056f54e618/f167a3bc-e140-4fa8-a8f8-326a99e0f567.png

가장 왼쪽 위, 즉 집이 있는 곳의 좌표는 (1, 1)로 나타내고 가장 오른쪽 아래, 즉 학교가 있는 곳의 좌표는 (m, n)으로 나타냅니다.

격자의 크기 m, n과 물이 잠긴 지역의 좌표를 담은 2차원 배열 puddles이 매개변수로 주어집니다. 오른쪽과 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단경로의 개수를 1,000,000,007로 나눈 나머지를 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 격자의 크기 m, n은 1 이상 100 이하인 자연수입니다.
    • m과 n이 모두 1인 경우는 입력으로 주어지지 않습니다.
  • 물에 잠긴 지역은 0개 이상 10개 이하입니다.
  • 집과 학교가 물에 잠긴 경우는 입력으로 주어지지 않습니다.

입출력 예

m n puddles return

4 3 [[2, 2]] 4

 

풀이

DP 문제에 자신이 없어서 도전했는데, 어떻게 접근해야 할지 몰라서 풀이를 찾아 봤다.

먼저 오른쪽과 아래쪽으로만 움직여 집에서 학교까지 갈 수 있는 최단 경로를 구하는 것 이므로, 왼쪽 또는 위로 이동을 고려하지 않아야 한다.

위의 사진을 보면, 오른쪽과 아래쪽으로 이동하는 경로가 있고, 화살표를 잘 보면 1,3 지점에서 아래쪽으로 두가지의 화살표, 2,2 지점에서 오른쪽으로 두가지의 화살표가 있는것을 볼 수 있다.

이를보면 i,j 위치의 경로는 [i-1,j] [i,j-1] 경로의 합 이라는 것을 알 수 있다.

이를통해 점화식을 세우고, 만약 현재 위치가 물에 잠긴 지역일경우 이동할 수 없으므로 스킵하도록 했다.

DP문제를 더 많이 접할 필요를 느꼈다…

코드

class Solution {
    public int solution(int m, int n, int[][] puddles) {
        int mod = 1000000007;
        int[][] arr = new int[n+1][m+1];

        for (int i = 0; i < puddles.length; i++) {
            int[] puddle = puddles[i];
            arr[puddle[1]][puddle[0]] = -1;
        }
        arr[1][1] = 1;
        for (int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                if(arr[i][j] == -1) continue;
                if(arr[i - 1][j] > 0) arr[i][j] += arr[i - 1][j] % mod;
                if(arr[i][j - 1] > 0) arr[i][j] += arr[i][j - 1] % mod;
            }
        }
        return arr[n][m] % mod;
    }
}

문제

문제 설명

두 개의 단어 begin, target과 단어의 집합 words가 있습니다. 아래와 같은 규칙을 이용하여 begin에서 target으로 변환하는 가장 짧은 변환 과정을 찾으려고 합니다.

1. 한 번에 한 개의 알파벳만 바꿀 수 있습니다. 2. words에 있는 단어로만 변환할 수 있습니다.

예를 들어 begin이 "hit", target가 "cog", words가 ["hot","dot","dog","lot","log","cog"]라면 "hit" -> "hot" -> "dot" -> "dog" -> "cog"와 같이 4단계를 거쳐 변환할 수 있습니다.

두 개의 단어 begin, target과 단어의 집합 words가 매개변수로 주어질 때, 최소 몇 단계의 과정을 거쳐 begin을 target으로 변환할 수 있는지 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 각 단어는 알파벳 소문자로만 이루어져 있습니다.
  • 각 단어의 길이는 3 이상 10 이하이며 모든 단어의 길이는 같습니다.
  • words에는 3개 이상 50개 이하의 단어가 있으며 중복되는 단어는 없습니다.
  • begin과 target은 같지 않습니다.
  • 변환할 수 없는 경우에는 0를 return 합니다.

입출력 예

begin target words return

"hit" "cog" ["hot", "dot", "dog", "lot", "log", "cog"] 4
"hit" "cog" ["hot", "dot", "dog", "lot", "log"] 0

입출력 예 설명

예제 #1

문제에 나온 예와 같습니다.

예제 #2

target인 "cog"는 words 안에 없기 때문에 변환할 수 없습니다.

풀이

BFS로 풀었다. 변환 횟수와 현재 문자열로 Pair를 만든 후에 큐에 추가한다, 해당 문자열을 변환 가능할 경우 변환 횟수를 1회 늘리고 변환 가능한 문자를 다시 큐에 추가한다. 이를 반복하여 큐에서 꺼낸 값이 target과 동일할 경우 리턴하도록 작성했다.

코드

import javafx.util.Pair;
import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
    public int solution(String begin, String target, String[] words) {
        int answer = 0;
        PriorityQueue<Pair<String,Integer>> queue = new PriorityQueue<>(new Comparator<Pair<String, Integer>>() {
            @Override
            public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
                return o1.getValue() - o2.getValue();
            }
        });
        boolean isContain = false;
        for (String word:
             words) {
            if (word.equals(target)) {
                isContain = true;
            }
        }
        if (!isContain)
            return 0;
        queue.add(new Pair<>(begin, 0));
        while (true) {
            Pair<String, Integer> p = queue.poll();
            if (p.getKey().equals(target)) {
                answer = p.getValue();
                break;
            }
            for (int i = 0; i < words.length; i++) {
                int diff = 0;
                for (int j = 0; j < p.getKey().length(); j++) {
                    if (words[i].charAt(j) != p.getKey().charAt(j)) {
                        diff++;
                    }
                }
                if (diff == 1) {
                    queue.add(new Pair<>(words[i], p.getValue()+1));
                }
            }
        }
        return answer;
    }
}

문제

문제 설명

자연수 n 개로 이루어진 중복 집합(multi set, 편의상 이후에는 "집합"으로 통칭) 중에 다음 두 조건을 만족하는 집합을 최고의 집합이라고 합니다.

  1. 각 원소의 합이 S가 되는 수의 집합
  2. 위 조건을 만족하면서 각 원소의 곱 이 최대가 되는 집합

예를 들어서 자연수 2개로 이루어진 집합 중 합이 9가 되는 집합은 다음과 같이 4개가 있습니다.

{ 1, 8 }, { 2, 7 }, { 3, 6 }, { 4, 5 }

그중 각 원소의 곱이 최대인 { 4, 5 }가 최고의 집합입니다.

집합의 원소의 개수 n과 모든 원소들의 합 s가 매개변수로 주어질 때, 최고의 집합을 return 하는 solution 함수를 완성해주세요.

제한사항

  • 최고의 집합은 오름차순으로 정렬된 1차원 배열(list, vector) 로 return 해주세요.
  • 만약 최고의 집합이 존재하지 않는 경우에 크기가 1인 1차원 배열(list, vector) 에 1 을 채워서 return 해주세요.
  • 자연수의 개수 n은 1 이상 10,000 이하의 자연수입니다.
  • 모든 원소들의 합 s는 1 이상, 100,000,000 이하의 자연수입니다.

입출력 예

n s result

2 9 [4, 5]
2 1 [-1]
2 8 [4, 4]

입출력 예 설명

입출력 예#1

문제의 예시와 같습니다.

입출력 예#2

자연수 2개를 가지고는 합이 1인 집합을 만들 수 없습니다. 따라서 -1이 들어있는 배열을 반환합니다.

입출력 예#3

자연수 2개로 이루어진 집합 중 원소의 합이 8인 집합은 다음과 같습니다.

{ 1, 7 }, { 2, 6 }, { 3, 5 }, { 4, 4 }

그중 각 원소의 곱이 최대인 { 4, 4 }가 최고의 집합입니다.

풀이

처음에는 문제만 읽고 중복 조합 문제라고 확신하고 문제를 풀었는데, 계속 시간초과가 나서 입력을 보니 n의 개수가 10,000 s가 100,000,000 이었다. 절대로 이렇게 풀면 안되는 문제였다.. n의 개수가 2에 s가 100,000,000 이라고 해도, 중복 조합을 이용했을 경우 s제곱만큼의 연산을 해야하므로.. 입출력을 잘 보고 문제를 풀도록 하자.

각 원소의 곱이 최대인 집합을 만들기 위해서는 각 원소들이 균등한 값을 가질 때 가장 높다. 첫번째 예제인 [4,5]만 봐도, [1,8], [2,7], [3,6], [4,5] 중에서 각 원소들의 차가 가장 적은 [4,5]의 곱이 가장 큰 것을 알 수 있다.

따라서 s / n으로 나눈 값을 전체 배열에다 채워준 후에 나머지값이 존재 할 경우 나머지 값의 크기만큼을 각 원소에 1씩 더해주면 된다. 이때 오름차순으로 정렬된 배열을 리턴해야 하므로, 전체 길이 - 나머지 값 부터 전체 길이까지 각 원소의 값을 1씩 더해주면 된다.

코드

import java.util.*;
class Solution {
   public int[] solution(int n, int s) {
        int[] answer;
        if (n > s) {
            answer = new int[1];
            answer[0] = -1;
        } else {
            answer = new int[n];
            int val = s / n;
            Arrays.fill(answer, val);
            if (s % n != 0) { 
                int a = s % n;
                for (int i = answer.length-a; i < answer.length; i++) {
                    answer[i]++;
                }
            }
        }
        return answer;
    }
}

문제

문제 설명

회사원 Demi는 가끔은 야근을 하는데요, 야근을 하면 야근 피로도가 쌓입니다. 야근 피로도는 야근을 시작한 시점에서 남은 일의 작업량을 제곱하여 더한 값입니다. Demi는 N시간 동안 야근 피로도를 최소화하도록 일할 겁니다.Demi가 1시간 동안 작업량 1만큼을 처리할 수 있다고 할 때, 퇴근까지 남은 N 시간과 각 일에 대한 작업량 works에 대해 야근 피로도를 최소화한 값을 리턴하는 함수 solution을 완성해주세요.

제한 사항

  • works는 길이 1 이상, 20,000 이하인 배열입니다.
  • works의 원소는 50000 이하인 자연수입니다.
  • n은 1,000,000 이하인 자연수입니다.

입출력 예

works n result

[4, 3, 3] 4 12
[2, 1, 2] 1 6
[1,1] 3 0

입출력 예 설명

입출력 예 #1

n=4 일 때, 남은 일의 작업량이 [4, 3, 3] 이라면 야근 지수를 최소화하기 위해 4시간동안 일을 한 결과는 [2, 2, 2]입니다. 이 때 야근 지수는 22 + 22 + 22 = 12 입니다.

입출력 예 #2

n=1일 때, 남은 일의 작업량이 [2,1,2]라면 야근 지수를 최소화하기 위해 1시간동안 일을 한 결과는 [1,1,2]입니다. 야근지수는 12 + 12 + 22 = 6입니다.

입출력 예 #3

남은 작업량이 없으므로 피로도는 0입니다.

풀이

야근피로도는 남아있는 작업량의 제곱의 합이므로, 야근 피로도가 가장 작아지기 위해서는 남아있는 작업량이 균등할수록 작아질 수 있다. 이를 위해 우선순위큐를 활용하여 남아있는 작업 중 작업량이 가장 큰 작업부터 차례대로 줄여나가도록 코드를 작성했다.

코드

import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
       public long solution(int n, int[] works) {
        long answer = 0;
        PriorityQueue<Integer> queue = new PriorityQueue<>(Comparator.reverseOrder());
        for (int i = 0; i < works.length; i++) {
            queue.add(works[i]);
        }
        while (n != 0 ) {
            if (queue.peek() == 0) break;
            queue.add(queue.poll()-1);
            n--;
        }
        while (!queue.isEmpty()) {
            int val = queue.poll();
            answer += val*val;
        }
        return answer;
    }
}

문제

문제 설명

네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있을 때 컴퓨터 A와 컴퓨터 C도 간접적으로 연결되어 정보를 교환할 수 있습니다. 따라서 컴퓨터 A, B, C는 모두 같은 네트워크 상에 있다고 할 수 있습니다.

컴퓨터의 개수 n, 연결에 대한 정보가 담긴 2차원 배열 computers가 매개변수로 주어질 때, 네트워크의 개수를 return 하도록 solution 함수를 작성하시오.

제한사항

  • 컴퓨터의 개수 n은 1 이상 200 이하인 자연수입니다.
  • 각 컴퓨터는 0부터 n-1인 정수로 표현합니다.
  • i번 컴퓨터와 j번 컴퓨터가 연결되어 있으면 computers[i][j]를 1로 표현합니다.
  • computer[i][i]는 항상 1입니다.

입출력 예

n computers return

3 [[1, 1, 0], [1, 1, 0], [0, 0, 1]] 2
3 [[1, 1, 0], [1, 1, 1], [0, 1, 1]] 1

입출력 예 설명

예제 #1

아래와 같이 2개의 네트워크가 있습니다.

예제 #2

아래와 같이 1개의 네트워크가 있습니다.

풀이

DFS 또는 BFS로 풀 수 있는 문제로, 나는 BFS를 사용해서 풀었다. 각 노드들을 순회하면서, 만약 방문하지 않은 노드라면 큐에 넣은 후 연결된 노드를 모두 탐색하여 방문처리 해준다. 방문처리가 끝났다면 answer 값을 증가 시켜주면 된다.

코드

import java.util.LinkedList;
import java.util.Queue;

class Solution {
    public int solution(int n, int[][] computers) {
        int answer = 0;
        boolean[] visited = new boolean[n];
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                queue.add(i);
                while (!queue.isEmpty()) {
                    int idx = queue.poll();
                    for (int j = 0; j < computers[idx].length; j++) {
                        if (!visited[j] && computers[idx][j] == 1) {
                            visited[j] = true;
                            queue.add(j);
                        }
                    }
                }
                answer++;
            }
        }

        return answer;
    }
}

+ Recent posts