diff --git "a/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj10216.java" "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj10216.java" new file mode 100644 index 00000000..94eb9d87 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj10216.java" @@ -0,0 +1,98 @@ +import java.io.*; +import java.util.*; + +public class boj10216 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 분리집합(서로소 집합)의 루트 노드들을 저장하는 집합 + private static Set set; + + // 유니온-파인드를 위한 배열 (x, y, r, parent) + private static int[][] uf; + + // 각 집합의 크기를 저장하는 배열 + private static int[] size; + + // 적군 진영의 수 + private static int n; + + public static void main(String[] args) throws IOException { + int t = Integer.parseInt(br.readLine()); // 테스트 케이스 개수 + + for (int i = 0; i < t; i++) { + init(); // 초기 데이터 설정 및 연결 관계 계산 + getDisjointSets(); // 분리된 집합의 수 계산 + sb.append(set.size()).append("\n"); // 결과 저장 + } + + bw.write(sb.toString()); // 모든 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하고 연결 관계를 계산하는 메소드 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 적군 진영의 수 + + // 배열 초기화 + uf = new int[n][4]; // [x좌표, y좌표, 통신 반경, 부모 인덱스] + size = new int[n]; // 각 집합의 크기 + set = new HashSet<>(); // 분리집합의 루트 노드 집합 + + // 각 진영 정보 입력 및 연결 관계 계산 + for (int i = 0; i < n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + uf[i][0] = Integer.parseInt(st.nextToken()); // x좌표 + uf[i][1] = Integer.parseInt(st.nextToken()); // y좌표 + uf[i][2] = Integer.parseInt(st.nextToken()); // 통신 반경 + uf[i][3] = i; // 자기 자신을 부모로 초기화 + size[i] = 1; // 초기 집합 크기는 1 + + // 이전에 입력받은 모든 진영과의 연결 여부 확인 + for (int j = 0; j < i; j++) { + // 두 진영의 통신 반경 합의 제곱이 두 점 사이의 거리의 제곱보다 크거나 같으면 연결 가능 + // (r1 + r2)^2 >= (x1 - x2)^2 + (y1 - y2)^2 + if ((uf[i][2] + uf[j][2]) * (uf[i][2] + uf[j][2]) + >= Math.abs(uf[i][0] - uf[j][0]) * Math.abs(uf[i][0] - uf[j][0]) + + Math.abs(uf[i][1] - uf[j][1]) * Math.abs(uf[i][1] - uf[j][1])) { + union(i, j); // 두 진영을 같은 집합으로 합침 + } + } + } + } + + // 두 집합을 합치는 메소드 + private static void union(int x, int y) { + int X = find(x); // x의 루트 찾기 + int Y = find(y); // y의 루트 찾기 + + if (X == Y) return; // 이미 같은 집합이면 종료 + + // 더 작은 집합을 더 큰 집합에 합침 (최적화) + if (size[X] < size[Y]) { + uf[X][3] = Y; // X의 부모를 Y로 설정 + size[Y] += size[X]; // Y 집합의 크기 증가 + } else { + uf[Y][3] = X; // Y의 부모를 X로 설정 + size[X] += size[Y]; // X 집합의 크기 증가 + } + } + + // 요소의 루트를 찾는 메소드 (경로 압축 사용) + private static int find(int x) { + if (uf[x][3] == x) return x; // 자기 자신이 루트인 경우 + return uf[x][3] = find(uf[x][3]); // 경로 압축 + } + + // 분리된 집합의 수를 계산하는 메소드 + private static void getDisjointSets() { + for (int i = 0; i < n; i++) { + int root = find(i); // 각 요소의 루트 찾기 + if (!set.contains(root)) set.add(root); // 새로운 루트면 집합에 추가 + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj1947.java" "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj1947.java" new file mode 100644 index 00000000..2646c051 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj1947.java" @@ -0,0 +1,41 @@ +import java.io.*; + +public class boj1947 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final int MOD = 1000000000; // 나머지 연산을 위한 모듈러 값 + private static long[] dp; // 완전 순열의 경우의 수를 저장할 배열 + private static int n; // 사람의 수 + + public static void main(String[] args) throws IOException { + init(); // 초기 설정 및 DP 계산 + bw.write(String.valueOf(dp[n])); // 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + // 완전 순열(어느 누구도 자신의 선물을 받지 않는 순열)의 경우의 수 계산 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 사람 수 입력 + dp = new long[n + 1]; // DP 배열 초기화 + + // 기본 케이스 설정 + dp[0] = 1; // 0명일 때는 1가지 경우 (아무도 없는 경우) + dp[1] = 0; // 1명일 때는 0가지 경우 (자신의 선물을 받지 않을 방법 없음) + + // 점화식을 이용한 DP 계산 + // 완전 순열의 점화식: D(n) = (n-1) * (D(n-1) + D(n-2)) + for (int i = 2; i <= n; i++) { + /* + * 이 점화식의 의미: + * 1. i번째 사람이 j번째 사람에게 선물을 줄 때(j != i) + * 2. 두 가지 경우가 발생: + * a. j번째 사람이 i번째 사람에게 선물을 줌 -> 나머지 n-2명에 대한 완전 순열 문제(dp[i-2]) + * b. j번째 사람이 i번째 사람에게 선물을 주지 않음 -> i와 j를 제외한 나머지에 대한 완전 순열(dp[i-1]) + * 3. 이를 j의 n-1가지 경우에 대해 모두 계산 + */ + dp[i] = (i-1)*(dp[i-1] + dp[i-2]) % MOD; // 오버플로우 방지를 위한 모듈러 연산 + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj2591.java" "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj2591.java" new file mode 100644 index 00000000..c29a73c5 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj2591.java" @@ -0,0 +1,57 @@ +import java.io.*; + +public class boj2591 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 동적 프로그래밍을 위한 배열 + private static int[] dp; // dp[i]: i길이까지의 숫자를 해석하는 경우의 수 + + // 입력된 숫자 카드의 길이 + private static int n; + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 및 DP 계산 + + // 결과 출력 + bw.write(String.valueOf(dp[n])); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 초기 데이터를 설정하고 DP를 계산하는 메소드 + * 1~34까지의 숫자로 해석하는 경우의 수를 계산합니다. + */ + private static void init() throws IOException { + String input = br.readLine(); // 숫자 카드 입력 + n = input.length(); // 숫자 카드 길이 + dp = new int[n + 1]; // DP 배열 초기화 + + // 기본 경우: 빈 문자열은 1가지 방법으로 해석 가능 + dp[0] = 1; + + // 각 위치까지의 해석 방법 수 계산 + for (int i = 1; i <= n; i++) { + char current = input.charAt(i - 1); // 현재 숫자 + + // 한 자리 숫자로 해석하는 경우 (0이 아닐 때만 가능) + if (current != '0') { + dp[i] += dp[i - 1]; // 이전 위치까지의 해석 방법 수를 더함 + } + + // 두 자리 숫자로 해석하는 경우 (두 번째 자리부터 가능) + if (i > 1) { + char prev = input.charAt(i - 2); // 이전 숫자 + int twoDigitNum = (prev - '0') * 10 + (current - '0'); // 두 자리 숫자 계산 + + // 두 자리 숫자가 10~34 범위이고 첫 자리가 0이 아닐 때 + if (prev != '0' && twoDigitNum >= 10 && twoDigitNum <= 34) { + dp[i] += dp[i - 2]; // 두 자리 전까지의 해석 방법 수를 더함 + } + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj4386.java" "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj4386.java" new file mode 100644 index 00000000..9a0f5e5e --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj4386.java" @@ -0,0 +1,119 @@ +import java.io.*; +import java.util.*; + +public class boj4386 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // stars[i][0]: i번째 별의 x좌표, stars[i][1]: i번째 별의 y좌표 + private static double[][] stars; + + // 각 별까지의 최소 거리를 저장하는 배열 + private static double[] dist; + + // 방문 여부를 체크하는 배열 + private static boolean[] visited; + + // 별의 개수 + private static int n; + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + + // 프림 알고리즘으로 최소 신장 트리 구성 + double answer = prim(0); + + // 소수점 둘째 자리까지 출력 (반올림) + bw.write(String.valueOf(Math.round(answer*100)/100.0)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 초기 데이터를 설정하는 메소드 + * 별의 개수와 좌표를 입력받아 초기화합니다. + */ + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 별의 개수 입력 + + // 배열 초기화 + stars = new double[n][2]; // 별 좌표 배열 + dist = new double[n]; // 최소 거리 배열 + visited = new boolean[n]; // 방문 체크 배열 + + // 최소 거리를 매우 큰 값으로 초기화 (무한대 역할) + Arrays.fill(dist, 1e6); + + // 각 별의 좌표 입력 + for (int i = 0; i < n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + double x = Double.parseDouble(st.nextToken()); + double y = Double.parseDouble(st.nextToken()); + stars[i][0] = x; + stars[i][1] = y; + } + } + + /** + * 프림 알고리즘을 사용하여 최소 신장 트리를 구성하는 메소드 + * @param start 시작 별의 인덱스 + * @return 최소 신장 트리의 가중치 합 (최소 비용) + */ + private static double prim(int start) { + // 우선순위 큐를 사용하여 거리가 가장 짧은 별부터 선택 + // [별 인덱스, 거리]를 저장 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> Double.compare(o1[1], o2[1])); + + double sum = 0.0; // 최소 신장 트리의 총 가중치 + + dist[start] = 0.0; // 시작 별까지의 거리는 0 + pq.add(new double[]{start, dist[start]}); // 시작 별을 큐에 추가 + + // 모든 별을 연결할 때까지 반복 + while (!pq.isEmpty()) { + double[] current = pq.poll(); // 거리가 가장 짧은 별 선택 + int currentStar = (int)current[0]; // 현재 별의 인덱스 + + // 이미 방문한 별이면 스킵 + if (visited[currentStar]) continue; + + // 현재 별 방문 처리 및 가중치 합산 + visited[currentStar] = true; + sum += current[1]; // 현재까지의 거리를 합산 + + // 다른 모든 별과의 거리 계산 + for (int i = 0; i < n; i++) { + // 자기 자신이거나 이미 방문한 별은 스킵 + if (i == currentStar || visited[i]) continue; + + // 현재 별에서 i번째 별까지의 거리 계산 + double newDist = distance( + stars[currentStar][0], stars[currentStar][1], + stars[i][0], stars[i][1] + ); + + // 기존 거리보다 더 짧으면 업데이트 + if (newDist < dist[i]) { + dist[i] = newDist; + pq.add(new double[]{i, dist[i]}); // 업데이트된 별을 큐에 추가 + } + } + } + + return sum; // 최소 신장 트리의 총 가중치 반환 + } + + /** + * 두 점 사이의 유클리드 거리를 계산하는 메소드 + * @param x1 첫 번째 점의 x좌표 + * @param y1 첫 번째 점의 y좌표 + * @param x2 두 번째 점의 x좌표 + * @param y2 두 번째 점의 y좌표 + * @return 두 점 사이의 거리 + */ + private static double distance(double x1, double y1, double x2, double y2) { + return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj5972.java" "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj5972.java" new file mode 100644 index 00000000..fa306b16 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:10\354\243\274\354\260\250/boj5972.java" @@ -0,0 +1,102 @@ +import java.io.*; +import java.util.*; + +public class boj5972 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 무한대를 표현하기 위한 상수 값 + private static final int MAX_INT = 50000005; + + // 그래프 표현을 위한 인접 리스트 + private static List> graph; + + // 각 정점까지의 최단 거리를 저장하는 배열 + private static int[] dist; + + // n: 정점의 수, m: 간선의 수 + private static int n, m; + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + dijkstra(); // 다익스트라 알고리즘으로 최단 경로 계산 + + // 목적지(n)까지의 최단 거리 출력 + int answer = dist[n]; + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 헛간의 개수 + m = Integer.parseInt(st.nextToken()); // 길의 개수 + + // 그래프 초기화 + graph = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + + // 거리 배열 초기화 + dist = new int[n + 1]; + Arrays.fill(dist, MAX_INT); // 모든 거리를 무한대로 초기화 + dist[1] = 0; // 시작점의 거리는 0 + + // 간선 정보 입력 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int n1 = Integer.parseInt(st.nextToken()); // 출발 헛간 + int n2 = Integer.parseInt(st.nextToken()); // 도착 헛간 + int cost = Integer.parseInt(st.nextToken()); // 소의 수(비용) + + // 양방향 그래프 구성 + graph.get(n1).add(new Node(n2, cost)); + graph.get(n2).add(new Node(n1, cost)); + } + } + + // 다익스트라 알고리즘으로 최단 경로를 계산하는 메소드 + private static void dijkstra() { + // 우선순위 큐를 이용한 다익스트라 알고리즘 + // 비용이 작은 순서대로 정렬 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> Integer.compare(o1.cost, o2.cost)); + + // 시작점을 큐에 추가 + pq.add(new Node(1, dist[1])); + + while (!pq.isEmpty()) { + Node current = pq.poll(); + + // 이미 처리된 노드는 스킵 + if (current.cost > dist[current.dest]) continue; + + // 현재 노드와 연결된 모든 노드 검사 + for (Node next : graph.get(current.dest)) { + int nDest = next.dest; // 다음 목적지 + int nCost = next.cost; // 다음 목적지까지의 비용 + + // 더 짧은 경로를 발견한 경우 업데이트 + if (dist[current.dest] + nCost < dist[nDest]) { + dist[nDest] = dist[current.dest] + nCost; + pq.add(new Node(nDest, dist[nDest])); + } + } + } + } + + // 그래프의 노드 정보를 저장하는 클래스 + static class Node { + int dest; // 목적지 노드 번호 + int cost; // 해당 노드까지의 비용(소의 수) + + Node (int dest, int cost) { + this.dest = dest; + this.cost = cost; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj1944.java" "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj1944.java" new file mode 100644 index 00000000..bb584931 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj1944.java" @@ -0,0 +1,207 @@ +import java.io.*; +import java.util.StringTokenizer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Queue; + +/** + * 백준 1944번 - 복제 로봇 + * 문제 요약: + * N×N 미로에서 로봇이 모든 열쇠(K)를 찾아 수집하는 최소 이동 횟수를 구하는 문제 + * 로봇의 시작 위치(S)와 열쇠 위치(K)가 주어지며, 0은 빈 칸, 1은 벽을 의미 + * MST(최소 신장 트리) 알고리즘을 활용하여 해결 + */ +public class boj1944 { + // 입출력을 위한 객체들 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 4방향 이동을 위한 dx, dy 배열 (동, 남, 서, 북) + private static final int[] dx = { 1, 0, -1, 0 }; + private static final int[] dy = { 0, 1, 0, -1 }; + + // 무한대 값 설정 (충분히 큰 값) + private static final int INF = (int) 7e6; + + // n: 미로의 크기, m: 열쇠의 개수 + private static int n, m; + + // 미로 정보를 저장할 2차원 배열 + private static char[][] map; + + // 시작 위치(S)와 열쇠 위치(K)를 저장할 리스트 + private static List keyPoints = new ArrayList<>(); + + // 각 지점 간의 최단 거리를 저장할 2차원 배열 + private static int[][] distances; + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + init(); // 입력 및 초기화 + int answer = prim(); // 프림 알고리즘으로 MST 구성 + + // 결과 출력 + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 변수를 초기화하는 메소드 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 미로의 크기 + m = Integer.parseInt(st.nextToken()); // 열쇠의 개수 + + // 미로 배열 초기화 + map = new char[n][n]; + + // 미로 정보 입력 + for (int i = 0; i < n; i++) { + String input = br.readLine(); + for (int j = 0; j < n; j++) { + map[i][j] = input.charAt(j); + + // 시작 위치(S)와 열쇠 위치(K)를 keyPoints 리스트에 추가 + if (map[i][j] == 'K' || map[i][j] == 'S') { + keyPoints.add(new int[] { i, j }); + } + } + } + + // 각 지점 간의 거리를 저장할 2차원 배열 초기화 + // m+1은 시작 위치(S)와 m개의 열쇠 위치(K)를 모두 포함 + distances = new int[m + 1][m + 1]; + for (int i = 0; i < m + 1; i++) { + Arrays.fill(distances[i], INF); // 모든 거리를A 무한대로 초기화 + distances[i][i] = 0; // 자기 자신까지의 거리는 0 + } + + // 모든 지점 간의 최단 거리 계산 + calculateAllDistances(); + } + + /** + * 모든 지점 간의 최단 거리를 BFS로 계산하는 메소드 + */ + private static void calculateAllDistances() { + // 각 시작 위치(S)와 열쇠 위치(K)에서 다른 모든 지점까지의 거리 계산 + for (int i = 0; i < m + 1; i++) { + int[] start = keyPoints.get(i); + BFS(start[0], start[1], i); // BFS로 최단 거리 계산 + } + } + + /** + * BFS로 특정 지점에서 다른 모든 지점까지의 최단 거리를 계산하는 메소드 + * @param startX 시작 지점의 x 좌표 + * @param startY 시작 지점의 y 좌표 + * @param startIdx 시작 지점의 인덱스 + */ + private static void BFS(int startX, int startY, int startIdx) { + Queue q = new ArrayDeque<>(); + boolean[][] visited = new boolean[n][n]; + + // 시작 지점을 큐에 추가 [x좌표, y좌표, 거리] + q.add(new int[] { startX, startY, 0 }); + visited[startX][startY] = true; + + while (!q.isEmpty()) { + int[] current = q.poll(); + int x = current[0]; + int y = current[1]; + int dist = current[2]; + + // 현재 위치가 시작 위치(S) 또는 열쇠 위치(K)인지 확인 + for (int i = 0; i < m + 1; i++) { + int[] point = keyPoints.get(i); + if (x == point[0] && y == point[1]) { + // 시작 지점에서 현재 지점까지의 거리 저장 + distances[startIdx][i] = dist; + } + } + + // 4방향 탐색 + for (int d = 0; d < 4; d++) { + int nx = x + dx[d]; + int ny = y + dy[d]; + + // 미로 범위를 벗어나거나 벽이거나 이미 방문한 지점이면 스킵 + if (OOB(nx, ny) || map[nx][ny] == '1' || visited[nx][ny]) { + continue; + } + + // 방문 표시 후 큐에 추가 + visited[nx][ny] = true; + q.add(new int[] { nx, ny, dist + 1 }); + } + } + } + + /** + * 좌표가 미로 범위를 벗어나는지 확인하는 메소드 + * @param nx x 좌표 + * @param ny y 좌표 + * @return 범위를 벗어나면 true, 그렇지 않으면 false + */ + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1; + } + + /** + * 프림 알고리즘으로 MST(최소 신장 트리)를 구성하는 메소드 + * @return MST의 총 가중치 (모든 열쇠를 수집하는 최소 이동 횟수) + */ + private static int prim() { + boolean[] visited = new boolean[m + 1]; // 방문 여부 배열 + int[] dist = new int[m + 1]; // 현재 MST에서 각 정점까지의 최소 거리 + + // 모든 거리를 무한대로 초기화 + Arrays.fill(dist, INF); + dist[0] = 0; // 시작 지점의 거리는 0 (인덱스 0은 시작 위치 S) + + // 우선순위 큐 초기화 (거리가 짧은 순으로 정렬) + PriorityQueue pq = new PriorityQueue<>((a, b) -> Integer.compare(a[1], b[1])); // [노드, 비용] + pq.add(new int[] { 0, 0 }); // 시작 지점 추가 [노드 인덱스, 비용] + int totalCost = 0; // MST의 총 가중치 + + // 프림 알고리즘 수행 + while (!pq.isEmpty()) { + int[] current = pq.poll(); + int node = current[0]; // 현재 노드 + int cost = current[1]; // 현재까지의 비용 + + // 이미 방문한 노드면 스킵 + if (visited[node]) + continue; + + // 노드 방문 처리 및 비용 누적 + visited[node] = true; + totalCost += cost; + + // 현재 노드에서 갈 수 있는 다른 노드들 탐색 + for (int next = 0; next < m + 1; next++) { + // 방문하지 않았고 현재 알고 있는 거리보다 더 짧은 경로가 있다면 갱신 + if (!visited[next] && distances[node][next] < dist[next]) { + dist[next] = distances[node][next]; + pq.add(new int[] { next, dist[next] }); + } + } + } + + // 모든 열쇠를 수집할 수 있는지 확인 + for (int i = 0; i < m + 1; i++) { + if (!visited[i]) + return -1; // 방문하지 못한 정점이 있으면 불가능 + } + + return totalCost; // MST의 총 가중치 반환 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj20055.java" "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj20055.java" new file mode 100644 index 00000000..19cc685f --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj20055.java" @@ -0,0 +1,130 @@ +import java.io.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.StringTokenizer; + +/** + * 백준 20055번 - 컨베이어 벨트 위의 로봇 + * 문제 요약: N칸의 컨베이어 벨트가 있고, 각 칸은 내구도가 있다. + * 로봇이 올라가고 내리면서 내구도가 감소하고, + * 내구도가 0인 칸이 K개 이상이 되면 종료되는 시뮬레이션 문제 + */ +public class boj20055 { + // 입출력을 위한 객체들 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 컨베이어 벨트를 양방향 큐(덱)로 표현 + private static Deque deque; + + // n: 컨베이어 벨트의 한 쪽 길이, k: 종료 조건인 내구도 0인 칸의 개수 + private static int n, k; + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + init(); // 입력 및 초기화 + + int answer = 0; // 단계 수를 저장할 변수 + int count = 0; // 내구도가 0인 칸의 개수 + + // 내구도가 0인 칸이 k개 미만인 동안 반복 + while (count < k) { + answer++; // 단계 증가 + + // 1. 벨트가 각 칸 위에 있는 로봇과 함께 한 칸 회전 + // pollLast()로 마지막 요소를 빼서 addFirst()로 맨 앞에 추가 + deque.addFirst(deque.pollLast()); + + // 2. 로봇 이동 및 내구도 체크, 새로운 내구도 0인 칸의 수 누적 + count += checkNAndMoveRobot(); + } + + // 결과 출력 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 변수를 초기화하는 메소드 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 컨베이어 벨트의 한 쪽 길이 + k = Integer.parseInt(st.nextToken()); // 종료 조건(내구도 0인 칸의 개수) + + deque = new ArrayDeque<>(); // 컨베이어 벨트를 표현할 덱 초기화 + + // 각 칸의 내구도 입력 받기 + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < 2*n; i++) { + // 내구도 값으로 Cell 객체 생성 후 덱에 추가 + deque.addLast(new Cell(Integer.parseInt(st.nextToken()))); + } + } + + /** + * 로봇 이동 규칙에 따라 로봇을 이동시키고 내구도를 체크하는 메소드 + * @return 새롭게 내구도가 0이 된 칸의 개수 + */ + private static int checkNAndMoveRobot() { + // 덱을 리스트로 변환하여 인덱스로 접근 가능하게 함 + List list = new ArrayList<>(deque); + int count = 0; // 새롭게 내구도가 0이 된 칸의 개수 + + // 내리는 위치(n-1)에 도달한, 로봇을 내림 + list.get(n - 1).onRobot = false; + + // 벨트 위 로봇들을 뒤에서부터 앞으로 이동시킴 (뒤에서부터 확인해야 중복 이동 방지) + for (int i = n - 2; i > 0; i--) { + // 현재 칸에 로봇이 있고, 다음 칸의 내구도가 0보다 크며 로봇이 없으면 + if (list.get(i).onRobot && + (list.get(i + 1).duration > 0 && !list.get(i + 1).onRobot)) { + + list.get(i + 1).duration--; // 다음 칸 내구도 감소 + + // 내구도가 0이 되었으면 카운트 증가 + if (list.get(i + 1).duration == 0) count++; + + list.get(i + 1).onRobot = true; // 다음 칸에 로봇 이동 + list.get(i).onRobot = false; // 현재 칸 로봇 제거 + } + } + + // 다시 한번 내리는 위치의 로봇 제거 (이동 후 내리는 위치에 도달한 로봇도 처리) + list.get(n - 1).onRobot = false; + + // 올리는 위치(0)에 로봇 올리기 + if (list.get(0).duration > 0) { + list.get(0).duration--; // 올리는 위치 내구도 감소 + + // 내구도가 0이 되었으면 카운트 증가 + if (list.get(0).duration == 0) count++; + + list.get(0).onRobot = true; // 올리는 위치에 로봇 올림 + } + + // 변경된 리스트를 다시 덱으로 변환 + deque = new ArrayDeque<>(list); + + return count; // 새롭게 내구도가 0이 된 칸의 개수 반환 + } + + /** + * 컨베이어 벨트의 각 칸을 표현하는 내부 클래스 + */ + static class Cell { + int duration; // 칸의 내구도 + boolean onRobot; // 로봇 존재 여부 + + Cell(int duration) { + this.duration = duration; + this.onRobot = false; // 초기에는 로봇이 없음 + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2307.java" "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2307.java" new file mode 100644 index 00000000..22a1663d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2307.java" @@ -0,0 +1,177 @@ +import java.io.*; +import java.util.StringTokenizer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.PriorityQueue; + +/** + * 백준 2307번 - 도로검문 + * 문제 요약: + * 1번 정점에서 n번 정점까지 가는 최단 경로를 구하고, + * 그 경로 상의 도로(간선)를 하나씩 차단했을 때 + * 1에서 n까지 가는 최단 경로의 지연 시간이 최대가 되는 값을 구하는 문제 + */ +public class boj2307 { + // 입출력을 위한 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 그래프를 표현하기 위한 인접 리스트 + private static List> graph; + + // 최단 거리를 저장할 배열 + private static int[] dist; + + // 최단 경로 복원을 위한 부모 노드 배열 (이전 노드 정보) + private static int[] parent; + + // n: 정점의 수, m: 간선의 수 + private static int n, m; + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + // 입력값 초기화 및 그래프 구성 + init(); + + // 초기 최단 경로 계산 (도로 차단 없는 상태) + // 0, 0은 차단할 도로가 없음을 의미 + dijkstra(0, 0); + + // 최단 경로 상의 각 도로를 차단했을 때 최대 지연 시간 계산 + // dist[n]은 초기 최단 경로의 거리 + int answer = block(dist[n]); + + // 결과 출력 + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 그래프를 초기화하는 메소드 + */ + private static void init() throws IOException { + // 정점 수와 간선 수 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 정점의 수 + m = Integer.parseInt(st.nextToken()); // 간선의 수 + + // 최단 거리 배열과 부모 노드 배열 초기화 + dist = new int[n + 1]; // 1~n까지의 인덱스 사용 + parent = new int[n + 1]; // 경로 복원을 위한 부모 노드 배열 + + // 그래프 초기화 (인접 리스트) + graph = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); // 각 정점마다 인접 리스트 생성 + } + + // 간선 정보 입력 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int u = Integer.parseInt(st.nextToken()); // 시작 정점 + int v = Integer.parseInt(st.nextToken()); // 도착 정점 + int w = Integer.parseInt(st.nextToken()); // 가중치(시간) + + // 양방향 그래프이므로 양쪽에 모두 간선 추가 + graph.get(u).add(new Node(v, w)); + graph.get(v).add(new Node(u, w)); + } + } + + /** + * 다익스트라 알고리즘으로 1번 정점에서 각 정점까지의 최단 경로 계산 + * @param u 차단할 도로의 시작 정점 (0이면 차단하지 않음) + * @param v 차단할 도로의 도착 정점 (0이면 차단하지 않음) + */ + private static void dijkstra(int u, int v) { + // 비용을 기준으로 오름차순 정렬하는 우선순위 큐 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> Integer.compare(o1.cost, o2.cost)); + + // 거리 배열 초기화 (무한대로 설정) + Arrays.fill(dist, (int)5e8); // 5 * 10^8는 실질적인 무한대 값 + + // 시작점(1번 정점)의 거리는 0으로 설정 + dist[1] = 0; + pq.add(new Node(1, dist[1])); // 시작점을 우선순위 큐에 추가 + + // 다익스트라 알고리즘 수행 + while (!pq.isEmpty()) { + // 현재 최단 거리가 가장 짧은 노드 선택 + Node current = pq.poll(); + + // 이미 처리된 노드면 스킵 (중복 방지) + if (current.cost > dist[current.dest]) continue; + + // 현재 노드의 인접 노드들 탐색 + for (Node next : graph.get(current.dest)) { + // 차단된 도로라면 스킵 + // (u,v)나 (v,u) 형태의 도로를 차단 + if ((current.dest == u && next.dest == v) || + (current.dest == v && next.dest == u)) continue; + + // 현재 노드를 거쳐 다음 노드로 가는 것이 더 짧은 경우 + if (dist[current.dest] + next.cost < dist[next.dest]) { + // 최단 거리 갱신 + dist[next.dest] = dist[current.dest] + next.cost; + // 경로 복원을 위해 부모 노드 정보 저장 + parent[next.dest] = current.dest; + // 우선순위 큐에 갱신된 정보 추가 + pq.add(new Node(next.dest, dist[next.dest])); + } + } + } + } + + /** + * 최단 경로 상의 각 도로를 차단했을 때 최대 지연 시간 계산 + * @param first 초기 최단 경로의 거리 + * @return 최대 지연 시간 (도달할 수 없으면 -1) + */ + private static int block(int first) { + // 원본 parent 배열 복사 (dijkstra 호출 시 parent가 변경되므로) + int[] originalParent = Arrays.copyOf(parent, parent.length); + + // 최단 경로 역추적을 위한 변수 + int u = n; // 도착점부터 시작 + int v = originalParent[u]; // u의 부모 노드 (경로상 이전 노드) + int time = 0; // 최대 지연 시간 + + // 시작점(1번 정점)에 도달할 때까지 반복 + while (u != 1) { + // u와 v 사이의 도로를 차단하고 다시 최단 경로 계산 + dijkstra(u, v); + + // n번 정점에 도달할 수 없는 경우 + if (dist[n] == (int)5e8) return -1; + + // 지연 시간 갱신 (현재 최단 경로 - 초기 최단 경로) + time = Math.max(time, dist[n] - first); + + // 다음 도로로 이동 (경로를 거슬러 올라감) + u = v; + v = originalParent[u]; + } + + return time; // 최대 지연 시간 반환 + } + + /** + * 그래프의 노드를 표현하는 클래스 + */ + static class Node { + int dest; // 도착 정점 + int cost; // 간선의 가중치(시간) + + // 생성자 + Node(int dest, int cost) { + this.dest = dest; + this.cost = cost; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2479.java" "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2479.java" new file mode 100644 index 00000000..064c4c0d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj2479.java" @@ -0,0 +1,113 @@ +import java.io.*; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.StringTokenizer; + +/** + * 백준 2479번 - 경로 찾기 + * 문제 요약: N개의 이진코드가 주어지고, 시작 코드에서 끝 코드까지 + * 해밍 거리가 1인 코드들만 거쳐서 갈 수 있는 최단 경로를 찾는 문제 + */ +public class boj2479 { + // 입출력을 위한 객체들 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + private static int[] arr; // 각 코드 번호의 이진값을 정수로 저장하는 배열 + private static boolean[] visited; // BFS 탐색 시 방문 여부를 체크하는 배열 + private static int n, k, start, end; // n: 코드 개수, k: 코드 길이, start: 시작 코드 번호, end: 도착 코드 번호 + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + init(); // 입력 및 초기화 + String answer = BFS(); // BFS로 경로 탐색 + bw.write(answer + "\n"); // 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 변수를 초기화하는 메소드 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 코드 개수 + k = Integer.parseInt(st.nextToken()); // 코드 길이 + + arr = new int[n + 1]; // 1번부터 n번까지의 코드를 저장 (인덱스 1부터 시작) + visited = new boolean[n + 1]; // 방문 여부 체크 배열 + + // 각 코드를 입력받아 2진수에서 10진수로 변환하여 저장 + for (int i = 1; i <= n; i++) { + arr[i] = Integer.parseInt(br.readLine(), 2); // 2진수 문자열을 10진수 정수로 변환 + } + + // 시작 코드와 도착 코드 번호 입력 + st = new StringTokenizer(br.readLine()); + start = Integer.parseInt(st.nextToken()); // 시작 코드 번호 + end = Integer.parseInt(st.nextToken()); // 도착 코드 번호 + } + + /** + * BFS를 이용하여 시작 코드에서 도착 코드까지의 경로를 찾는 메소드 + * @return 경로를 공백으로 구분한 문자열, 경로가 없으면 "-1" 반환 + */ + private static String BFS() { + Queue q = new ArrayDeque<>(); // BFS를 위한 큐 + visited[start] = true; // 시작 코드 방문 체크 + + // 시작 코드의 값과 경로 문자열을 가진 Element 객체 생성하여 큐에 추가 + Element e = new Element(arr[start], "" + start); + q.add(e); + + // BFS 탐색 시작 + while(!q.isEmpty()) { + Element current = q.poll(); // 큐에서 하나 꺼냄 + + // 현재 코드가 도착 코드와 같다면 경로 반환 + if (current.num == arr[end]) return current.s; + + // 모든 코드에 대해 검사 + for (int i = 1; i <= n; i++) { + // 이미 방문했거나, 해밍 거리가 1이 아니면 스킵 + if (visited[i] || !IsHammingDistanceOne(current.num, arr[i])) continue; + + visited[i] = true; // 방문 체크 + // 새로운 코드와 업데이트된 경로를 큐에 추가 + q.add(new Element(arr[i], current.s + " " + i)); + } + } + + return "-1"; // 경로를 찾지 못했을 경우 + } + + /** + * 두 이진 코드 간의 해밍 거리가 1인지 확인하는 메소드 + * 해밍 거리: 같은 길이의 두 이진 코드에서 서로 다른 비트의 개수 + * @param x 첫 번째 코드 (정수 표현) + * @param y 두 번째 코드 (정수 표현) + * @return 해밍 거리가 1이면 true, 아니면 false + */ + public static boolean IsHammingDistanceOne(int x, int y) { + // XOR 연산으로 서로 다른 비트만 1이 되고, bitCount로 1의 개수(=다른 비트 수)를 계산 + return Integer.bitCount(x ^ y) == 1; + } + + /** + * BFS 탐색 시 사용되는 Element 클래스 + * 현재 코드 값과 시작점부터 현재까지의 경로를 저장 + */ + static class Element { + int num; // 코드의 정수 값 + String s; // 경로 문자열 (코드 번호들을 공백으로 구분) + + Element(int num, String s) { + this.num = num; + this.s = s; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj7662.java" "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj7662.java" new file mode 100644 index 00000000..7d665083 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:11\354\243\274\354\260\250/boj7662.java" @@ -0,0 +1,87 @@ +import java.io.*; +import java.util.StringTokenizer; +import java.util.TreeMap; + +/** + * 백준 7662번 - 이중 우선순위 큐 + * 문제 요약: 최댓값과 최솟값을 모두 추출할 수 있는 이중 우선순위 큐를 구현하는 문제 + * 명령어 I n: 정수 n을 큐에 삽입 + * 명령어 D 1: 큐에서 최댓값 삭제 + * 명령어 D -1: 큐에서 최솟값 삭제 + */ +public class boj7662 { + // 입출력을 위한 객체들 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static StringBuilder sb = new StringBuilder(); // 결과를 모아서 한 번에 출력하기 위한 StringBuilder + + // TreeMap을 사용하여 이중 우선순위 큐 구현 (key: 정수값, value: 해당 정수의 개수) + private static TreeMap tMap; + + // 각 테스트 케이스의 연산 개수 + private static int k; + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + int t = Integer.parseInt(br.readLine()); // 테스트 케이스의 개수 + + // 각 테스트 케이스마다 처리 + for (int i = 0; i < t; i++) { + init(); // 테스트 케이스 초기화 및 처리 + } + + // 모든 결과를 한 번에 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 테스트 케이스를 초기화하고 처리하는 메소드 + */ + private static void init() throws IOException { + k = Integer.parseInt(br.readLine()); // 연산의 개수 + tMap = new TreeMap<>(); // TreeMap 초기화 (자동으로 키 정렬) + + // k개의 연산 수행 + for (int i = 0; i < k; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + char command = st.nextToken().charAt(0); // 명령어 (I 또는 D) + int num = Integer.parseInt(st.nextToken()); // 정수 또는 삭제 위치 지정(-1 또는 1) + + if (command == 'I') { // 삽입 연산 + // 이미 존재하는 값이면 개수 증가, 없으면 1로 설정 + tMap.put(num, tMap.getOrDefault(num, 0) + 1); + } else { // 삭제 연산 + // 큐가 비어있으면 무시 + if (tMap.isEmpty()) continue; + + if (num == 1) { // 최댓값 삭제 + int maxKey = tMap.lastKey(); // TreeMap의 마지막 키(최댓값) + tMap.put(maxKey, tMap.get(maxKey) - 1); // 개수 감소 + + // 개수가 0이 되면 해당 키 제거 + if (tMap.get(maxKey) == 0) tMap.remove(maxKey); + } else { // 최솟값 삭제 + int minKey = tMap.firstKey(); // TreeMap의 첫번째 키(최솟값) + tMap.put(minKey, tMap.get(minKey) - 1); // 개수 감소 + + // 개수가 0이 되면 해당 키 제거 + if (tMap.get(minKey) == 0) tMap.remove(minKey); + } + } + } + + // 결과를 StringBuilder에 추가 + if (tMap.isEmpty()) { + sb.append("EMPTY"); // 큐가 비어있으면 "EMPTY" 출력 + } else { + // 최댓값과 최솟값을 공백으로 구분하여 출력 + sb.append(tMap.lastKey()).append(" ").append(tMap.firstKey()); + } + sb.append("\n"); // 테스트 케이스의 결과 구분을 위한 개행 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj1405.java" "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj1405.java" new file mode 100644 index 00000000..3b4b0ca5 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj1405.java" @@ -0,0 +1,98 @@ +import java.io.*; +import java.util.StringTokenizer; + +public class boj1405 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 4방향 이동을 위한 x, y 좌표 변화량 (동, 서, 남, 북) + private static final int[] dx = {0, 0, 1, -1}; + private static final int[] dy = {1, -1, 0, 0}; + + // 방문 여부를 저장하는 배열 + private static boolean[][] visited; + + // 각 방향으로 이동할 확률 (%) + private static int[] moveRate; + + // 이동할 총 횟수 + private static int n; + + // 로봇이 단순하지 않은 경로로 이동할 확률 + private static double answer; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // 시작 위치(14, 14)를 방문 처리 + visited[14][14] = true; + + // DFS 탐색 시작: 시작 좌표(14, 14), 이동 카운트 0, 초기 확률 1 + dfs(14, 14, 0, 1); + + // 결과 출력 + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력 데이터를 초기화하는 함수 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + // 첫 번째 숫자는 총 이동 횟수 n + n = Integer.parseInt(st.nextToken()); + + // 이동 확률 배열 초기화 + moveRate = new int[4]; + + // 방문 배열 초기화 (시작 위치를 중앙에 두고 충분한 크기로 설정) + visited = new boolean[29][29]; + + // 동, 서, 남, 북 방향으로의 이동 확률 입력 받기 + for (int i = 0; i < 4; i++) { + moveRate[i] = Integer.parseInt(st.nextToken()); + } + } + + /** + * DFS로 모든 가능한 경로를 탐색하는 함수 + * + * @param x 현재 x 좌표 + * @param y 현재 y 좌표 + * @param count 현재까지 이동한 횟수 + * @param rate 현재 경로의 확률 + */ + private static void dfs(int x, int y, int count, double rate) { + // 기저 조건: n번 이동했으면 해당 경로의 확률을 더함 + if (count == n) { + answer += rate; + return; + } + + // 네 방향으로의 이동 시도 + for (int i = 0; i < 4; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + + // 해당 방향으로 이동할 확률이 0이면 건너뜀 + if (moveRate[i] == 0) continue; + + // 아직 방문하지 않은 위치라면 + if (!visited[nx][ny]) { + // 방문 처리 + visited[nx][ny] = true; + + // 다음 위치로 이동하며 확률 갱신 (현재 확률 * 해당 방향 이동 확률) + dfs(nx, ny, count + 1, rate * ((double)moveRate[i]/100)); + + // 백트래킹: 방문 기록 원복 + visited[nx][ny] = false; + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj21278.java" "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj21278.java" new file mode 100644 index 00000000..ad63835d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj21278.java" @@ -0,0 +1,124 @@ +import java.io.*; +import java.util.StringTokenizer; + +public class boj21278 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 무한대 값을 표현하기 위한 상수 + private static final int INF = (int)1e7; + + // 각 지점 간의 최단 거리를 저장하는 배열 + private static int[][] dist; + + // 정답을 저장하는 배열 [도시1, 도시2, 최소시간] + private static int[] answer; + + // n: 도시의 수, m: 도로의 수 + private static int n, m; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // 플로이드-워셜 알고리즘으로 모든 쌍 최단 경로 찾기 + floydWarshall(); + + // 모든 두 도시 쌍에 대해 최소 시간 계산 + for (int i = 1; i < n; i++) { + for (int j = i + 1; j <= n; j++) { + // i번 도시와 j번 도시에 경찰서를 세웠을 때 총 소요 시간 + int time = calTime(i, j); + + // 기존 최소 시간보다 작으면 정답 갱신 + if (answer[2] > time) { + answer[0] = i; // 첫 번째 경찰서 위치 + answer[1] = j; // 두 번째 경찰서 위치 + answer[2] = time; // 최소 시간 + } + } + } + + // 결과 출력 (왕복 시간이므로 *2) + sb.append(answer[0]).append(" ").append(answer[1]).append(" ").append(answer[2] * 2); + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력 데이터를 초기화하는 함수 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 도시의 수 + m = Integer.parseInt(st.nextToken()); // 도로의 수 + + // 거리 배열 초기화 + dist = new int[n + 1][n + 1]; + + // 정답 배열 초기화 + answer = new int[3]; + answer[2] = INF; // 최소 시간을 무한대로 초기화 + + // 거리 배열의 모든 값을 무한대로 초기화 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + dist[i][j] = INF; + } + dist[i][i] = 0; // 자기 자신까지의 거리는 0 + } + + // 도로 정보 입력 받기 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int x = Integer.parseInt(st.nextToken()); + int y = Integer.parseInt(st.nextToken()); + dist[x][y] = 1; // x에서 y로 가는 도로 (가중치 1) + dist[y][x] = 1; // y에서 x로 가는 도로 (양방향) + } + } + + /** + * 플로이드-워셜 알고리즘으로 모든 쌍 최단 경로를 계산하는 함수 + */ + private static void floydWarshall() { + // k: 중간 경유 도시 + for (int k = 1; k <= n; k++) { + // i: 출발 도시 + for (int i = 1; i <= n; i++) { + // j: 도착 도시 + for (int j = 1; j <= n; j++) { + // i에서 k를 거쳐 j로 가는 경로가 더 짧으면 갱신 + if (dist[i][k] > 0 && dist[k][j] > 0) { + dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]); + } + } + } + } + } + + /** + * 두 도시에 경찰서를 세웠을 때 총 소요 시간을 계산하는 함수 + * @param n1 첫 번째 경찰서 위치 + * @param n2 두 번째 경찰서 위치 + * @return 모든 도시에서 가장 가까운 경찰서까지의 거리 합 + */ + private static int calTime(int n1, int n2) { + int sum = 0; + + // 모든 도시에 대해 + for (int i = 1; i <= n; i++) { + // 경찰서가 있는 도시는 제외 + if (i == n1 || i == n2) continue; + + // i번 도시에서 가장 가까운 경찰서까지의 거리를 더함 + sum += Math.min(dist[n1][i], dist[n2][i]); + } + + return sum; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj3079.java" "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj3079.java" new file mode 100644 index 00000000..fe489b3d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj3079.java" @@ -0,0 +1,84 @@ +import java.io.*; +import java.util.StringTokenizer; + +public class boj3079 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + private static final int INF = (int)1e7; + private static int[][] dist; + private static int[] answer; + private static int n, m; + + public static void main(String[] args) throws IOException { + init(); + floydWarshall(); + + for (int i = 1; i < n; i++) { + for (int j = i + 1; j <= n; j++) { + int time = calTime(i, j); + if (answer[2] > time) { + answer[0] = i; + answer[1] = j; + answer[2] = time; + } + } + } + + sb.append(answer[0]).append(" ").append(answer[1]).append(" ").append(answer[2] * 2); + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + + dist = new int[n + 1][n + 1]; + answer = new int[3]; + answer[2] = INF; + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + dist[i][j] = INF; + } + dist[i][i] = 0; + } + + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int x = Integer.parseInt(st.nextToken()); + int y = Integer.parseInt(st.nextToken()); + + dist[x][y] = 1; + dist[y][x] = 1; + } + + } + + private static void floydWarshall() { + for (int k = 1; k <= n; k++) { + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (dist[i][k] > 0 && dist[k][j] > 0) { + dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]); + } + } + } + } + } + + private static int calTime(int n1, int n2) { + int sum = 0; + for (int i = 1; i <= n; i++) { + if (i == n1 || i == n2) continue; + + sum += Math.min(dist[n1][i], dist[n2][i]); + } + + return sum; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj7682.java" "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj7682.java" new file mode 100644 index 00000000..d79036d0 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:12\354\243\274\354\260\250/boj7682.java" @@ -0,0 +1,99 @@ +import java.io.*; + +public class boj7682 { + // 표준 입력을 위한 BufferedReader 객체 생성 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + // 표준 출력을 위한 BufferedWriter 객체 생성 + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 결과를 저장할 StringBuilder 객체 생성 (문자열 연산 효율성을 위해 사용) + private static final StringBuilder sb = new StringBuilder(); + // 틱택토 게임판을 나타내는 2차원 배열 + private static char[][] map; + + public static void main(String[] args) throws IOException { + // 무한 루프를 통해 여러 게임 케이스를 처리 + while (true) { + String input = br.readLine(); // 한 줄 입력 받기 + if (input.equals("end")) break; // "end"가 입력되면 루프 종료 + + // 입력된 문자열을 이용해 게임 종료 상태가 유효한지 검사 + if (endGame(input.toCharArray())) sb.append("valid"); + else sb.append("invalid"); + + sb.append("\n"); // 결과 사이에 줄바꿈 추가 + } + + // 모든 결과를 출력하고 리소스 정리 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 틱택토 게임의 종료 상태가 유효한지 검사하는 메서드 + * + * @param input 게임판 상태를 나타내는 문자 배열 (길이 9, 'X', 'O', '.'로 구성) + * @return 게임의 종료 상태가 유효하면 true, 아니면 false + */ + private static boolean endGame(char[] input) { + // 3x3 게임판 초기화 + map = new char[3][3]; + int xcnt = 0; // 'X'의 개수를 저장할 변수 + int ocnt = 0; // 'O'의 개수를 저장할 변수 + int index = 0; // 입력 배열의 인덱스 + + // 1차원 입력 배열을 2차원 게임판으로 변환하며 'X'와 'O'의 개수 세기 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + map[i][j] = input[index++]; + if (map[i][j] == 'X') xcnt++; + if (map[i][j] == 'O') ocnt++; + } + } + + // 규칙 1: 'X'는 'O'보다 0개 또는 1개 많아야 함 (X가 먼저 시작하므로) + if (!(xcnt == ocnt || xcnt == ocnt + 1)) return false; + + // 규칙 2: 'O'가 이겼다면, 'X'와 'O'의 개수는 같아야 함 + // (O가 이긴 직후 게임이 종료되고, O는 항상 두 번째로 두므로) + if (xcnt == ocnt && check8way(map, 'O') && !check8way(map, 'X')) return true; + + // 규칙 3: 'X'가 이겼다면, 'X'는 'O'보다 1개 많아야 함 + // (X가 이긴 직후 게임이 종료되고, X는 항상 첫 번째로 두므로) + if (xcnt > ocnt && check8way(map, 'X') && !check8way(map, 'O')) return true; + + // 규칙 4: 게임판이 가득 찼고(X 5개, O 4개) 'O'가 이기지 않았다면 유효한 상태 + if (xcnt == 5 && ocnt == 4 && !check8way(map, 'O')) return true; + + // 위 조건들을 모두 만족하지 않으면 유효하지 않은 상태 + return false; + } + + /** + * 주어진 플레이어(X 또는 O)가 승리했는지 확인하는 메서드 + * 가로, 세로, 대각선 방향으로 같은 문자가 3개 연속되어 있는지 확인 + * + * @param map 게임판 상태 + * @param c 확인할 플레이어 ('X' 또는 'O') + * @return 해당 플레이어가 승리했으면 true, 아니면 false + */ + private static boolean check8way(char[][] map, char c) { + // 가로줄과 세로줄 확인 + for (int i = 0; i < 3; i++) { + // i번째 가로줄이 모두 같은 문자인지 확인 + if (map[i][0] == c && map[i][0] == map[i][1] && map[i][1] == map[i][2]) return true; + // i번째 세로줄이 모두 같은 문자인지 확인 + if (map[0][i] == c && map[0][i] == map[1][i] && map[1][i] == map[2][i]) return true; + } + + // 좌상단에서 우하단 대각선 확인 + if (map[0][0] == c && map[0][0] == map[1][1] && map[1][1] == map[2][2]) return true; + + // 우상단에서 좌하단 대각선 확인 + if (map[0][2] == c && map[0][2] == map[1][1] && map[2][0] == map[1][1]) return true; + + // 승리 조건을 만족하지 않으면 false 반환 + return false; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj1561.java" "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj1561.java" new file mode 100644 index 00000000..9d384d6f --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj1561.java" @@ -0,0 +1,119 @@ +import java.io.*; +import java.util.*; + +public class boj1561 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 놀이기구 별 운행 시간을 저장하는 배열 + private static int[] arr; + + // n: 방문객 수, m: 놀이기구 수, answer: 마지막 방문객이 타게 될 놀이기구 번호 + private static int n, m, answer; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // 이진 탐색으로 마지막 방문객이 놀이기구를 타는 시간과 놀이기구 번호 구하기 + binarySearch(); + + // 결과 출력 + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력 데이터를 초기화하는 함수 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 방문객 수 + m = Integer.parseInt(st.nextToken()); // 놀이기구 수 + + // 각 놀이기구의 운행 시간 입력 + arr = new int[m]; + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < m; i++) { + arr[i] = Integer.parseInt(st.nextToken()); + } + } + + /** + * 이진 탐색을 이용해 마지막 방문객이 놀이기구를 타는 시간을 찾는 함수 + */ + private static void binarySearch() { + long left = 0; // 최소 시간 + long right = (long)6e14; // 최대 시간 (충분히 큰 값) + + // 이진 탐색 수행 + while (left <= right) { + long mid = left + (right - left) / 2; // 중간값 계산 (오버플로우 방지) + + // mid 시간에 n명을 모두 태울 수 있는지 확인 + if (valid(mid)) { + right = mid - 1; // 가능하면 더 작은 시간도 가능한지 확인 + } else { + left = mid + 1; // 불가능하면 더 큰 시간이 필요 + } + } + } + + /** + * 주어진 시간(target)에 n명의 방문객을 모두 태울 수 있는지 확인하고, + * 가능하다면 마지막 방문객이 타게 될 놀이기구 번호를 찾는 함수 + * + * @param target 검증할 시간 + * @return target 시간에 n명을 모두 태울 수 있으면 true, 아니면 false + */ + private static boolean valid(long target) { + // 특수 케이스: n이 m 이하이고 시간이 0이면, 처음에 모든 방문객이 놀이기구에 바로 탑승 + if (n <= m && target == 0) { + answer = n; // 마지막 방문객은 n번째 놀이기구에 탑승 + return true; + } + + // 각 놀이기구가 태울 수 있는 방문객 수와 마지막 방문객 탑승 시간 계산을 위한 배열 + long count[] = new long[m]; // 각 놀이기구가 태울 수 있는 방문객 수 + Arrays.fill(count, 1); // 초기에 각 놀이기구는 1명씩 태움 + long time[] = new long[m]; // 각 놀이기구의 마지막 방문객 탑승 시간 + long max = 0; // 가장 늦게 탑승하는 시간 + long totalCount = 0; // 총 태울 수 있는 방문객 수 + + // 각 놀이기구가 target 시간까지 태울 수 있는 방문객 수 계산 + for (int i = 0; i < m; i++) { + count[i] += target / arr[i]; // target 시간 동안 i번 놀이기구가 추가로 태울 수 있는 방문객 수 + totalCount += count[i]; // 모든 놀이기구가 태울 수 있는 총 방문객 수 + } + + // 모든 놀이기구가 target 시간까지 태울 수 있는 방문객 수가 n보다 작으면 false + if (totalCount < n) + return false; + + // target-1 시간까지 태운 방문객 수 계산 (이전 시간까지의 누적) + long beforeCount = m; // 초기에 각 놀이기구에 1명씩 타므로 m명 + for (int i = 0; i < m; i++) { + beforeCount += (target - 1) / arr[i]; // target-1 시간까지 i번 놀이기구가 추가로 태운 방문객 수 + time[i] = (count[i] - 1) * arr[i]; // i번 놀이기구의 마지막 방문객 탑승 시간 + max = Math.max(max, time[i]); // 가장 늦게 탑승하는 시간 갱신 + } + + // 마지막 방문객이 타게 될 놀이기구 찾기 + long remaining = n - beforeCount; // target 시간에 새로 탑승하는 방문객 수 + for (int i = 0; i < m; i++) { + // 마지막에 동시에 운행을 마치는 놀이기구 중에서 번호가 가장 작은 것을 찾음 + if (time[i] == max) { + remaining--; + if (remaining == 0) { + answer = i + 1; // 놀이기구 번호는 1부터 시작하므로 +1 + return true; + } + } + } + return true; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16118.java" "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16118.java" new file mode 100644 index 00000000..143b64b0 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16118.java" @@ -0,0 +1,187 @@ +import java.io.*; +import java.util.*; + +public class boj16118 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 그래프를 표현하는 인접 리스트 + private static List[] graph; + + // 여우와 늑대의 최단 거리를 저장하는 배열 + private static long[] distFox; // 여우의 각 노드까지의 최단 거리 + private static long[][] distWolf; // 늑대의 각 노드까지의 최단 거리 [노드번호][상태] + // 상태: 0 - 빠른 상태로 도착, 1 - 느린 상태로 도착 + + // n: 그래프의 정점 수, m: 그래프의 간선 수 + private static int n, m; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // 여우의 최단 경로 계산 (시작점: 1번 노드) + dijkstraFox(1); + + // 늑대의 최단 경로 계산 (시작점: 1번 노드) + dijkstraWolf(1); + + // 여우가 늑대보다 먼저 도착할 수 있는 노드 수 계산 + int answer = 0; + for (int i = 2; i <= n; i++) { + long fox = distFox[i]; // 여우가 i번 노드에 도착하는 최단 시간 + long wolf = Math.min(distWolf[i][0], distWolf[i][1]); // 늑대가 i번 노드에 도착하는 최단 시간 (두 상태 중 더 빠른 시간) + + // 여우가 늑대보다 빠르게 도착할 수 있으면 카운트 증가 + if (fox < wolf) answer++; + } + + // 결과 출력 + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력 데이터를 초기화하는 함수 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 정점의 수 + m = Integer.parseInt(st.nextToken()); // 간선의 수 + + // 그래프와 최단 거리 배열 초기화 + graph = new List[n + 1]; + distFox = new long[n + 1]; + distWolf = new long[n + 1][2]; // [노드번호][상태] - 상태 0: 빠른 속도, 상태 1: 느린 속도 + + // 그래프의 각 노드별 인접 리스트 생성 및 거리 배열 초기화 + for (int i = 0; i <= n; i++) { + graph[i] = new ArrayList<>(); + Arrays.fill(distWolf[i], (long)2e10); // 늑대 거리 배열을 매우 큰 값으로 + } + + // 간선 정보 입력 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int u = Integer.parseInt(st.nextToken()); // 시작 정점 + int v = Integer.parseInt(st.nextToken()); // 도착 정점 + int w = Integer.parseInt(st.nextToken()); // 가중치(거리) + + // 양방향 그래프 생성 (모든 간선 가중치를 2배로 저장) + // 이는 늑대가 느린 상태일 때 정확히 0.5배 속도로 이동할 수 있게 하기 위함 + graph[u].add(new Node(v, w * 2)); + graph[v].add(new Node(u, w * 2)); + } + } + + /** + * 여우의 최단 경로를 계산하는 다익스트라 알고리즘 + * @param start 시작 노드 + */ + private static void dijkstraFox(int start) { + // 우선순위 큐를 이용한 다익스트라 알고리즘 구현 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> Long.compare(o1.cost, o2.cost)); + + // 최단 거리 배열 초기화 + Arrays.fill(distFox, (long)2e10); + distFox[start] = 0; + + // 시작 노드 큐에 삽입 + pq.add(new Node(start, distFox[start])); + + while (!pq.isEmpty()) { + Node current = pq.poll(); + + // 이미 처리된 노드면 스킵 + if (current.cost > distFox[current.dest]) continue; + + // 현재 노드와 연결된 모든 노드에 대해 + for (Node next : graph[current.dest]) { + int nDest = next.dest; + long nCost = next.cost; + + // 더 짧은 경로를 발견하면 최단 거리 갱신 및 큐에 추가 + if (distFox[current.dest] + nCost < distFox[nDest]) { + distFox[nDest] = distFox[current.dest] + nCost; + pq.add(new Node(nDest, distFox[nDest])); + } + } + } + } + + /** + * 늑대의 최단 경로를 계산하는 다익스트라 알고리즘 + * 늑대는 빠른 상태와 느린 상태를 번갈아가며 이동 + * @param start 시작 노드 + */ + private static void dijkstraWolf(int start) { + // 우선순위 큐를 이용한 다익스트라 알고리즘 구현 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> Long.compare(o1.cost, o2.cost)); + + // 늑대는 빠른 상태(flag=1)로 시작 + distWolf[start][0] = 0; + pq.add(new Node(start, distWolf[start][0], 1)); // flag 1: 빠른 상태 + + while (!pq.isEmpty()) { + Node current = pq.poll(); + + // 이미 처리된 노드면 스킵 + if (current.flag == 1 && current.cost > distWolf[current.dest][0]) continue; + if (current.flag == -1 && current.cost > distWolf[current.dest][1]) continue; + + // 현재 노드와 연결된 모든 노드에 대해 + for (Node next : graph[current.dest]) { + int nDest = next.dest; + long nCost; + + // 현재 상태에 따라 다음 간선의 비용 계산 + // flag=1 (빠른 상태)일 때는 2배 빠름 (간선 비용/2) + // flag=-1 (느린 상태)일 때는 0.5배 느림 (간선 비용*2) + if (current.flag == 1) nCost = next.cost / 2; + else nCost = next.cost * 2; + + // 다음 상태는 현재 상태의 반대 + int nFlag = current.flag * -1; + + // 상태에 따라 최단 거리 갱신 + // nFlag=1 (다음에 빠른 상태)이면 distWolf[nDest][0] 갱신 + if (nFlag == 1 && distWolf[current.dest][1] + nCost < distWolf[nDest][0]) { + distWolf[nDest][0] = distWolf[current.dest][1] + nCost; + pq.add(new Node(nDest, distWolf[nDest][0], nFlag)); + } + + // nFlag=-1 (다음에 느린 상태)이면 distWolf[nDest][1] 갱신 + if (nFlag == -1 && distWolf[current.dest][0] + nCost < distWolf[nDest][1]) { + distWolf[nDest][1] = distWolf[current.dest][0] + nCost; + pq.add(new Node(nDest, distWolf[nDest][1], nFlag)); + } + } + } + } + + /** + * 그래프의 노드 정보를 저장하는 클래스 + */ + static class Node { + int dest; // 도착 노드 + long cost; // 간선 비용 + int flag; // 늑대의 상태 (1: 빠른 상태, -1: 느린 상태, 0: 여우) + + // 여우용 생성자 + Node(int dest, long cost) { + this.dest = dest; + this.cost = cost; + this.flag = 0; + } + + // 늑대용 생성자 + public Node(int dest, long cost, int flag) { + this.dest = dest; + this.cost = cost; + this.flag = flag; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16940.java" "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16940.java" new file mode 100644 index 00000000..6be186cf --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj16940.java" @@ -0,0 +1,123 @@ +import java.io.*; +import java.util.*; + +public class boj16940 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 그래프를 표현하는 인접 리스트 + private static List[] graph; + + // 검증할 BFS 순서 + private static int[] answer; + + // 방문 여부를 체크하는 배열 + private static boolean[] visited; + + // 정점의 개수 + private static int n; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // BFS 순서 검증 (맞으면 1, 틀리면 0 출력) + int k = BFS(1) ? 1 : 0; + bw.write(String.valueOf(k)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력 데이터를 초기화하는 함수 + */ + private static void init() throws IOException { + // 정점의 개수 입력 + n = Integer.parseInt(br.readLine()); + + // 배열 및 그래프 초기화 + answer = new int[n]; // 검증할 BFS 순서 + visited = new boolean[n + 1]; // 정점 방문 여부 (1-indexed) + graph = new List[n + 1]; // 그래프 인접 리스트 (1-indexed) + + // 각 정점의 인접 리스트 생성 + for (int i = 1; i <= n; i++) { + graph[i] = new ArrayList<>(); + } + + // 간선 정보 입력 (n-1개) + for (int i = 0; i < n - 1; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int u = Integer.parseInt(st.nextToken()); + int v = Integer.parseInt(st.nextToken()); + + // 양방향 그래프이므로 양쪽에 추가 + graph[u].add(v); + graph[v].add(u); + } + + // 검증할 BFS 순서 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + for (int i = 0; i < n; i++) { + answer[i] = Integer.parseInt(st.nextToken()); + } + } + + /** + * BFS 순서 검증 함수 + * @param start 시작 정점 + * @return 주어진 순서가 유효한 BFS 순서이면 true, 아니면 false + */ + private static boolean BFS(int start) { + // BFS를 위한 큐 + Queue q = new ArrayDeque<>(); + + // 현재 레벨에서 방문 가능한 정점들을 저장하는 집합 + Set set = new HashSet<>(); + + // answer 배열의 인덱스 (첫 정점은 무조건 1이므로 다음 인덱스인 1부터 시작) + int index = 1; + + // 시작 정점 방문 처리 및 큐에 추가 + visited[start] = true; + q.add(start); + + // BFS 실행 + while (!q.isEmpty() && index < n) { + // 현재 정점 꺼내기 + int current = q.poll(); + + // 현재 정점의 모든 인접 정점 확인 + for (int next : graph[current]) { + // 이미 방문한 정점은 건너뛰기 + if (visited[next]) continue; + + // 방문 처리 (큐에 바로 넣지 않고 set에 추가) + visited[next] = true; + set.add(next); + } + + // 현재 레벨에서 방문 가능한 정점들 중에서 주어진 순서대로 처리 + while (!set.isEmpty() && index < n) { + // 다음으로 방문해야 할 정점 (주어진 순서에 따라) + int next = answer[index]; + + // 다음 정점이 현재 레벨에서 방문 가능한 정점이 아니라면 순서가 잘못됨 + if (!set.contains(next)) { + return false; + } + + // 방문 처리 및 큐에 추가 + visited[next] = true; + set.remove(next); + q.add(next); + index++; + } + } + + // 모든 과정이 정상적으로 완료되면 유효한 BFS 순서 + return true; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj23290.java" "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj23290.java" new file mode 100644 index 00000000..ec54729b --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:13\354\243\274\354\260\250/boj23290.java" @@ -0,0 +1,332 @@ +import java.io.*; +import java.util.*; + +public class boj23290 { + // 입출력을 위한 버퍼 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 상어 이동을 위한 방향 배열 (상하좌우: 1234) + private static final int[] sdx = {0, -1, 0, 1, 0}; // 상어 x축 이동 방향 + private static final int[] sdy = {0, 0, -1, 0, 1}; // 상어 y축 이동 방향 + + // 물고기 이동을 위한 방향 배열 (←, ↖, ↑, ↗, →, ↘, ↓, ↙: 1~8) + private static final int[] dx = {0, 0, -1, -1, -1, 0, 1, 1, 1}; // 물고기 x축 이동 방향 + private static final int[] dy = {0, -1, -1, 0, 1, 1, 1, 0, -1}; // 물고기 y축 이동 방향 + + // 게임판과 관련된 변수들 + private static Cell[][] map; // 현재 게임판 + private static Cell[][] copy; // 복제용 게임판 + private static List list; // 상어가 이동할 수 있는 경로 저장 리스트 + private static boolean[][] visited; // 상어 이동 시 방문 여부 확인 + private static Shark shark; // 상어 객체 + private static int m, s; // m: 물고기 수, s: 마법 시전 횟수 + + public static void main(String[] args) throws IOException { + // 초기 설정 및 입력 처리 + init(); + + // s번 마법 시전 + while (s-- > 0) { + copyMap(); // 1. 물고기 복제 마법 준비 + moveFishes(); // 2. 물고기 이동 + moveShark(); // 3. 상어 이동 (물고기 냠냠) + removeSmell(); // 4. 물고기 냄새 제거 + copyFish(); // 5. 복제 마법 완료 + } + + // 물고기 수 세기 + int answer = countFishes(); + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 초기 설정 및 입력을 처리하는 함수 + */ + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + m = Integer.parseInt(st.nextToken()); // 물고기 수 + s = Integer.parseInt(st.nextToken()); // 마법 시전 횟수 + + // 게임판 초기화 (1-indexed, 4x4 크기) + map = new Cell[5][5]; + + // 셀 객체 생성 + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + map[i][j] = new Cell(); + } + } + + // 물고기 정보 입력 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int x = Integer.parseInt(st.nextToken()); // 물고기 행 위치 + int y = Integer.parseInt(st.nextToken()); // 물고기 열 위치 + int dir = Integer.parseInt(st.nextToken()); // 물고기 방향 + map[x][y].fishes.add(dir); // 해당 위치에 물고기 추가 + } + + // 상어 위치 입력 + st = new StringTokenizer(br.readLine()); + shark = new Shark(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken())); + } + + /** + * 현재 게임판을 복사하는 함수 (물고기 복제 마법 준비 단계) + */ + private static void copyMap() { + copy = new Cell[5][5]; + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + copy[i][j] = new Cell(); + + // 현재 맵의 물고기들을 복사 맵에 복제 + for (int fish : map[i][j].fishes) { + copy[i][j].fishes.add(fish); + } + } + } + } + + /** + * 물고기들을 이동시키는 함수 + */ + private static void moveFishes() { + // 이동 후 상태를 저장할 임시 게임판 + Cell[][] temp = new Cell[5][5]; + + // 임시 게임판 초기화 (냄새 정보 복사) + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + temp[i][j] = new Cell(); + temp[i][j].smell = map[i][j].smell; + } + } + + // 모든 위치의 물고기들을 확인하며 이동 + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + if (!map[i][j].fishes.isEmpty()) { + boolean canMove = false; + // 각 물고기의 방향에 대해 + for (int dir : map[i][j].fishes) { + int count = 0; + // 최대 8방향을 시도 (반시계 방향으로 45도씩 회전) + while (count < 8) { + int nx = i + dx[dir]; + int ny = j + dy[dir]; + + // 이동할 수 있는 조건: 맵 안이고, 상어가 없고, 냄새가 없어야 함 + if (!OOB(nx, ny) && !gotoShark(nx, ny) && !leftSmell(nx, ny)){ + temp[nx][ny].fishes.add(dir); + canMove = true; + break; + } + + // 이동할 수 없으면 45도 반시계 회전 + dir--; + if (dir == 0) dir = 8; + count++; + } + + // 8방향 모두 이동할 수 없으면 제자리에 남음 + if (!canMove) { + for (int fdir : map[i][j].fishes) { + temp[i][j].fishes.add(fdir); + } + } + } + } + } + } + + // 이동 완료된 임시 게임판을 현재 게임판으로 갱신 + map = temp; + } + + /** + * 상어를 이동시키는 함수 + */ + private static void moveShark() { + visited = new boolean[5][5]; + list = new ArrayList<>(); + int eatFishes = 0; + List eatenFishesPos = new ArrayList<>(); + + // DFS를 통해 상어의 모든 가능한 3칸 이동 경로 탐색 + dfs(0, shark.x, shark.y, eatFishes, eatenFishesPos, ""); + + // 가장 많은 물고기를 먹을 수 있는 경로 선택 + // 같은 수의 물고기를 먹을 수 있다면 사전순으로 가장 앞선 경로 선택 + Collections.sort(list, (o1, o2) -> { + if (o1.eatFishes == o2.eatFishes) return o1.dirs.compareTo(o2.dirs); + return Integer.compare(o2.eatFishes, o1.eatFishes); + }); + + // 선택된 경로로 상어 이동 및 물고기 제거 + Status nextPos = list.get(0); + shark.x = nextPos.x; + shark.y = nextPos.y; + for (int[] pos : nextPos.eatenFishesPos) { + map[pos[0]][pos[1]].smell = 3; // 냄새 남기기 (3턴 지속) + map[pos[0]][pos[1]].fishes.clear(); // 물고기 제거 + } + } + + /** + * 상어가 이동할 수 있는 모든 경로를 탐색하는 DFS 함수 + * @param index 이동 횟수 (0~2) + * @param x 현재 x 좌표 + * @param y 현재 y 좌표 + * @param eatFishes 현재까지 먹은 물고기 수 + * @param eatenFishesPos 물고기를 먹은 위치 목록 + * @param dirs 이동 방향 문자열 (예: "123") + */ + private static void dfs(int index, int x, int y, int eatFishes, List eatenFishesPos, String dirs) { + // 3칸 이동 완료 시 결과 저장 + if (index == 3) { + List temp = new ArrayList<>(); + for (int[] pos : eatenFishesPos) { + temp.add(pos); + } + list.add(new Status(x, y, eatFishes, temp, dirs)); + return; + } + + // 4방향(상하좌우) 탐색 + for (int i = 1; i <= 4; i++) { + int nx = x + sdx[i]; + int ny = y + sdy[i]; + boolean flag = false; + + // 맵을 벗어나면 건너뛰기 + if (OOB(nx, ny)) continue; + + // 물고기가 있고 아직 방문하지 않은 위치라면 + if (!map[nx][ny].fishes.isEmpty() && !visited[nx][ny]) { + eatFishes += map[nx][ny].fishes.size(); // 물고기 먹기 + eatenFishesPos.add(new int[]{nx, ny}); // 위치 기록 + visited[nx][ny] = true; // 방문 처리 + flag = true; // 상태 변경 표시 + } + + // 다음 이동 진행 + dfs(index + 1, nx, ny, eatFishes, eatenFishesPos, dirs + "" + i); + + // 상태를 변경했다면 백트래킹 + if (flag) { + eatFishes -= map[nx][ny].fishes.size(); // 물고기 수 복원 + eatenFishesPos.remove(eatenFishesPos.size() - 1); // 위치 기록 삭제 + visited[nx][ny] = false; // 방문 처리 복원 + } + } + } + + /** + * 해당 위치가 상어가 있는 위치인지 확인하는 함수 + */ + private static boolean gotoShark(int nx, int ny) { + return nx == shark.x && ny == shark.y; + } + + /** + * 해당 위치에 물고기 냄새가 있는지 확인하는 함수 + */ + private static boolean leftSmell(int nx, int ny) { + return map[nx][ny].smell > 0; + } + + /** + * 해당 위치가 맵을 벗어나는지 확인하는 함수 (Out Of Bounds) + */ + private static boolean OOB(int nx, int ny) { + return nx < 1 || nx > 4 || ny < 1 || ny > 4; + } + + /** + * 물고기 냄새를 감소시키는 함수 (매 턴마다 1씩 감소) + */ + private static void removeSmell() { + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + if (map[i][j].smell > 0) map[i][j].smell--; + } + } + } + + /** + * 복제 마법을 완료하는 함수 (복사해둔 물고기를 현재 맵에 추가) + */ + private static void copyFish() { + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + for (int fish : copy[i][j].fishes) { + map[i][j].fishes.add(fish); + } + } + } + } + + /** + * 현재 맵에 있는 총 물고기 수를 세는 함수 + */ + private static int countFishes() { + int cnt = 0; + for (int i = 1; i <= 4; i++) { + for (int j = 1; j <= 4; j++) { + cnt += map[i][j].fishes.size(); + } + } + + return cnt; + } + + /** + * 상어의 이동 상태를 저장하는 클래스 + */ + static class Status { + int x; // 최종 x 위치 + int y; // 최종 y 위치 + int eatFishes; // 먹은 물고기 수 + List eatenFishesPos; // 물고기를 먹은 위치들 + String dirs; // 이동 방향 문자열 + + public Status(int x, int y, int eatFishes, List eatenFishesPos, String dirs) { + this.x = x; + this.y = y; + this.eatFishes = eatFishes; + this.eatenFishesPos = eatenFishesPos; + this.dirs = dirs; + } + } + + /** + * 게임판의 각 셀을 나타내는 클래스 + */ + static class Cell { + List fishes; // 현재 위치의 물고기 방향 목록 + int smell; // 물고기 냄새 지속 시간 + + Cell() { + this.fishes = new ArrayList<>(); + this.smell = 0; + } + } + + /** + * 상어 객체를 나타내는 클래스 + */ + static class Shark { + int x; // 상어의 x 좌표 + int y; // 상어의 y 좌표 + + Shark(int x, int y) { + this.x = x; + this.y = y; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj11058.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj11058.java" new file mode 100644 index 00000000..9affaa44 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj11058.java" @@ -0,0 +1,44 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class boj11058 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static long[] dp; // dp[i]: i번 눌렀을 때 얻을 수 있는 최대 문자 개수 + private static int n; // 키를 누르는 횟수 + + public static void main(String[] args) throws IOException { + n = Integer.parseInt(br.readLine()); // 키를 누르는 횟수 입력 + dp = new long[n + 1]; // dp 배열 초기화 + dp(); // dp 계산 + + // 결과 출력 및 스트림 닫기 + bw.write(dp[n] + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + private static void dp() { + // i: 현재까지 누른 키의 횟수 + for (int i = 1; i <= n; i++) { + // 기본적으로는 A를 하나 추가하는 경우 + dp[i] = dp[i - 1] + 1; + + // Ctrl+A, Ctrl+C, Ctrl+V를 사용할 수 있는 경우 (최소 7번 이상 눌러야 함) + if (i > 6) { + // j: 복사-붙여넣기를 반복하는 횟수 + for (int j = 1; j < i - 3; j++) { + // dp[i-j-2]: Ctrl+A, Ctrl+C를 누르기 전까지의 상태 + // (j+1): 복사된 내용이 j번 붙여넣어지므로 j+1배가 됨 + dp[i] = Math.max(dp[i], dp[i - j - 2] * (j + 1)); + } + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16469.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16469.java" new file mode 100644 index 00000000..b94f4cf5 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16469.java" @@ -0,0 +1,128 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.LinkedList; +import java.util.Queue; + +public class boj16469 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 4방향 탐색을 위한 방향 배열 (하,우,상,좌) + private static final int[] dx = {1, 0, -1, 0}; + private static final int[] dy = {0, 1, 0, -1}; + private static final StringBuilder sb = new StringBuilder(); + + // map[x][y][0~2]: 각 악당의 이동 거리, map[x][y][3]: 벽 여부 + private static int[][][] map; + // visited[x][y][i]: i번째 악당의 방문 여부 + private static boolean[][][] visited; + // BFS를 위한 큐 + private static Queue queue = new LinkedList<>(); + private static int r, c; // 격자의 크기 + private static int count = 0; // 모든 악당이 만날 수 있는 지점의 개수 + private static int min = Integer.MAX_VALUE; // 최소 시간 + + public static void main(String[] args) throws IOException { + setting(); // 입력 처리 및 초기 설정 + + // 세 악당의 시작 위치 입력 받고 큐에 추가 + for (int i = 0; i < 3; i++) { + String[] input = br.readLine().split(" "); + int x = Integer.parseInt(input[0]); + int y = Integer.parseInt(input[1]); + visited[x][y][i] = true; // 시작 위치 방문 체크 + queue.add(new int[]{x, y, i}); // 큐에 [x좌표, y좌표, 악당번호] 추가 + } + + bfs(); // BFS로 각 악당의 이동 경로 탐색 + + // 결과 출력 + if (count == 0) { // 만날 수 있는 지점이 없는 경우 + min = -1; + sb.append(min).append("\n"); + } + else { // 만날 수 있는 지점이 있는 경우 + sb.append(min).append("\n").append(count).append("\n"); + } + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력 처리 및 초기 설정 + private static void setting() throws IOException { + String[] input = br.readLine().split(" "); + r = Integer.parseInt(input[0]); + c = Integer.parseInt(input[1]); + + // 3차원 배열 초기화 ([좌표][좌표][악당/벽]) + map = new int[r + 1][c + 1][4]; + visited = new boolean[r + 1][c + 1][3]; + + // 격자 정보 입력 (벽 정보는 map[x][y][3]에 저장) + for (int i = 1; i <= r; i++) { + input = br.readLine().split(""); + for (int j = 1; j <= c; j++) { + map[i][j][3] = Integer.parseInt(input[j - 1]); + } + } + } + + // BFS로 각 악당의 이동 경로 탐색 + private static void bfs() { + int time = 0; // 현재 시간 + + while (!queue.isEmpty()) { + int length = queue.size(); // 현재 시간에 처리할 위치의 수 + time++; + + // 현재 시간에 있는 모든 위치에 대해 처리 + for (int i = 0; i < length; i++) { + int[] current = queue.poll(); + + // 4방향 탐색 + for (int j = 0; j < 4; j++) { + int nx = current[0] + dx[j]; + int ny = current[1] + dy[j]; + + // 범위를 벗어나거나, 벽이거나, 이미 방문한 경우 스킵 + if (OOB(nx, ny) || map[nx][ny][3] == 1 || visited[nx][ny][current[2]]) continue; + + visited[nx][ny][current[2]] = true; // 방문 체크 + // 이동 거리 갱신 + map[nx][ny][current[2]] = map[current[0]][current[1]][current[2]] + 1; + queue.add(new int[]{nx, ny, current[2]}); // 다음 위치 큐에 추가 + } + } + + // 세 악당이 만날 수 있는 지점 찾기 + boolean found = false; + for (int i = 1; i <= r; i++) { + for (int j = 1; j <= c; j++) { + // 세 악당이 모두 도달할 수 있는 지점인지 확인 + if (visited[i][j][0] && visited[i][j][1] && visited[i][j][2]) { + found = true; + count++; // 만날 수 있는 지점 개수 증가 + } + } + } + + // 만날 수 있는 지점을 찾았다면 최소 시간 갱신 + if (found) { + min = Math.min(min, time); + break; + } + } + } + + // 주어진 좌표가 격자 범위를 벗어나는지 체크 + private static boolean OOB(int nx, int ny) { + return nx < 1 || nx > r || ny < 1 || ny > c; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16929.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16929.java" new file mode 100644 index 00000000..459ebc1e --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj16929.java" @@ -0,0 +1,102 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class boj16929 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 4방향 탐색을 위한 방향 배열 (우,하,좌,상) + private static final int[] dx = {1, 0, -1, 0}; + private static final int[] dy = {0, 1, 0, -1}; + // 결과 문자열을 만들기 위한 StringBuilder + private static final StringBuilder sb = new StringBuilder(); + + // 게임 보드와 방문 체크 배열 + private static char[][] map; // 게임 보드 배열 + private static boolean[][] visited; // 방문 체크 배열 + private static int n, m; // 보드의 크기 (n X m) + + public static void main(String[] args) throws IOException { + setting(); // 입력 처리 및 초기 세팅 + + // 모든 좌표를 시작점으로 사이클 찾기 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + visited = new boolean[n][m]; // 각 시작점마다 방문 배열 초기화 + // 아직 사이클을 찾지 못했고(sb.length() == 0), 현재 위치에서 사이클 발견시 + if (sb.length() == 0 && dfs(i, j, i, j, 0)) { + sb.append("Yes"); + break; + } + } + } + + // 사이클을 찾지 못한 경우 + if (sb.length() == 0) { + sb.append("No"); + } + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString() + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력 처리 및 초기 세팅을 위한 메소드 + private static void setting() throws IOException{ + // 보드의 크기 입력 받기 + String[] input = br.readLine().split(" "); + n = Integer.parseInt(input[0]); + m = Integer.parseInt(input[1]); + + // 게임 보드 초기화 + map = new char[n][m]; + + // 게임 보드 정보 입력 받기 + for (int i = 0; i < n; i++) { + input = br.readLine().split(""); + for (int j = 0; j < m; j++) { + map[i][j] = input[j].charAt(0); + } + } + } + + // DFS로 사이클 찾기 + // x, y: 현재 위치 + // startX, startY: 시작 위치 + // cnt: 현재까지의 이동 횟수 + private static boolean dfs(int x, int y, int startX, int startY, int cnt) { + visited[x][y] = true; // 현재 위치 방문 체크 + + // 4방향 탐색 + for(int i = 0; i < 4; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + + // 범위를 벗어나거나 다른 색상인 경우 스킵 + if (OOB(nx, ny) || map[nx][ny] != map[x][y]) continue; + + // 시작점으로 돌아왔고, 이동 횟수가 4 이상인 경우 (사이클 발견) + if (nx == startX && ny == startY && cnt >= 3) { + return true; + } + + // 아직 방문하지 않은 위치라면 탐색 계속 + if (!visited[nx][ny]) { + if (dfs(nx, ny, startX, startY, cnt + 1)) { + return true; + } + } + } + return false; // 사이클을 찾지 못한 경우 + } + + // 주어진 좌표가 보드 범위를 벗어나는지 체크 + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > m - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18222.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18222.java" new file mode 100644 index 00000000..042f6ea5 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18222.java" @@ -0,0 +1,47 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class boj18222 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static long k; // 찾고자 하는 위치 + + public static void main(String[] args) throws IOException { + k = Long.parseLong(br.readLine()); // 위치 k 입력 + int answer = function(k); // k번째 위치의 값 계산 + + // 결과 출력 및 스트림 닫기 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // k번째 위치의 값을 재귀적으로 계산하는 함수 + private static int function(long k) { + // 기저 사례: k가 1이면 0 반환 + if (k == 1) { + return 0; + } + + // k보다 작은 가장 큰 2의 거듭제곱수 찾기 + long len = 1; + while (len * 2 < k) { + len *= 2; + } + + // 문자열의 앞부분과 뒷부분이 반전된 관계이므로 + // k에서 len을 뺀 위치의 값을 반전시켜 반환 + if (function(k - len) == 0) { + return 1; + } + else { + return 0; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18234.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18234.java" new file mode 100644 index 00000000..1faececf --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj18234.java" @@ -0,0 +1,62 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Arrays; + +public class boj18234 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static int[][] carrots; // 당근 정보 배열 [당근의 크기][성장 속도] + private static int n; // 당근의 개수 + private static long t; // 주어진 시간 + + public static void main(String[] args) throws IOException { + setting(); // 입력 처리 및 초기 설정 + long answer = function(); // 수확할 수 있는 당근 크기의 합 계산 + + // 결과 출력 및 스트림 닫기 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력 처리 및 초기 설정 + private static void setting() throws IOException { + // 당근의 개수와 시간 입력 + String[] input = br.readLine().split(" "); + n = Integer.parseInt(input[0]); + t = Long.parseLong(input[1]); + + // 당근 정보 배열 초기화 + carrots = new int[n][2]; + + // 각 당근의 초기 크기와 성장 속도 입력 + for (int i = 0; i < n; i++) { + input = br.readLine().split(" "); + carrots[i][0] = Integer.parseInt(input[0]); // 초기 크기 + carrots[i][1] = Integer.parseInt(input[1]); // 성장 속도 + } + + // 당근을 성장 속도 기준으로 오름차순 정렬 + Arrays.sort(carrots, (o1, o2) -> o1[1] - o2[1]); + } + + // 수확할 수 있는 당근 크기의 합 계산 + private static long function() { + long answer = 0; + + // 성장 속도가 낮은 당근부터 순서대로 수확 + for (int i = 0; i < n; i++) { + // i번째 당근의 최종 크기 계산 + // 초기 크기 + (성장 속도 * 남은 시간) + // 남은 시간 = 전체 시간 - (아직 수확하지 않은 당근 수) + answer += carrots[i][0] + (carrots[i][1] * (t - n + i)); + } + return answer; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj21610.java" "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj21610.java" new file mode 100644 index 00000000..456aedee --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:2\354\243\274\354\260\250/boj21610.java" @@ -0,0 +1,181 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; + +public class boj21610 { + // 입출력을 위한 객체들 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 8방향 이동을 위한 방향 배열 (0번 인덱스는 dummy) + // ←, ↖, ↑, ↗, →, ↘, ↓, ↙ 순서 + private static final int[] dx = {0, 0, -1, -1, -1, 0, 1, 1, 1}; + private static final int[] dy = {0, -1, -1, 0, 1, 1, 1, 0, -1}; + + private static List clouds = new ArrayList<>(); // 구름의 위치를 저장하는 리스트 + private static List moves = new ArrayList<>(); // 이동 명령을 저장하는 리스트 + private static int[][] arr; // 격자의 물의 양을 저장하는 배열 + private static int n, m; // 격자의 크기 n, 이동 횟수 m + + public static void main(String[] args) throws IOException { + setting(); // 입력 처리 및 초기 설정 + + // 모든 이동 명령에 대해 시뮬레이션 + for (int[] move : moves) { + moveClouds(move); // 구름 이동 + raining(); // 비 내리기 + copyWater(); // 물 복사 버그 + makeClouds(); // 새로운 구름 생성 + } + + // 격자에 남아있는 물의 양의 합 출력 + bw.write(sum() + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력 처리 및 초기 설정 + private static void setting() throws IOException { + String[] input = br.readLine().split(" "); + n = Integer.parseInt(input[0]); + m = Integer.parseInt(input[1]); + + // 격자 초기화 + arr = new int[n + 1][n + 1]; + + // 격자의 물의 양 입력 + for (int i = 1; i <= n; i++) { + input = br.readLine().split(" "); + for (int j = 1; j <= n; j++) { + arr[i][j] = Integer.parseInt(input[j - 1]); + } + } + + // 이동 명령 입력 + for (int i = 0; i < m; i++) { + input = br.readLine().split(" "); + moves.add(new int[]{Integer.parseInt(input[0]), Integer.parseInt(input[1])}); + } + + // 초기 구름 위치 설정 + clouds.add(new int[]{n, 1}); + clouds.add(new int[]{n, 2}); + clouds.add(new int[]{n - 1, 1}); + clouds.add(new int[]{n - 1, 2}); + } + + // 구름 이동 + private static void moveClouds(int[] move) { + for (int i = 0; i < clouds.size(); i++) { + // 방향과 거리에 따라 구름 이동 + clouds.get(i)[0] += dx[move[0]] * move[1]; + clouds.get(i)[1] += dy[move[0]] * move[1]; + + // x좌표가 범위를 벗어난 경우 처리 + if (OOBX(clouds.get(i)[0])) { + if (clouds.get(i)[0] < 1) { + clouds.get(i)[0] = n - Math.abs(clouds.get(i)[0]) % n; + } + else if (clouds.get(i)[0] > n) { + clouds.get(i)[0] = clouds.get(i)[0] % n; + if (clouds.get(i)[0] == 0) { + clouds.get(i)[0] = n; + } + } + } + + // y좌표가 범위를 벗어난 경우 처리 + if (OOBY(clouds.get(i)[1])) { + if (clouds.get(i)[1] < 1) { + clouds.get(i)[1] = n - Math.abs(clouds.get(i)[1]) % n; + } + else if (clouds.get(i)[1] > n) { + clouds.get(i)[1] = clouds.get(i)[1] % n; + if (clouds.get(i)[1] == 0) { + clouds.get(i)[1] = n; + } + } + } + } + } + + // 격자 범위 체크 + private static boolean OOBX(int x) { + return x < 1 || x > n; + } + + private static boolean OOBY(int y) { + return y < 1 || y > n; + } + + // 비 내리기 (구름이 있는 칸의 물의 양 1 증가) + private static void raining() { + for (int[] cloud : clouds) { + arr[cloud[0]][cloud[1]]++; + } + } + + // 물 복사 버그 (대각선 방향으로 거리가 1인 칸에 물이 있는 바구니 수만큼 물의 양 증가) + private static void copyWater() { + List temp = new ArrayList<>(); + + for (int[] cloud : clouds) { + int count = 0; + + // 대각선 4방향 체크 + if (cloud[0] - 1 > 0 && cloud[1] - 1 > 0 && arr[cloud[0] - 1][cloud[1] - 1] > 0) + count++; + if (cloud[0] + 1 < n + 1 && cloud[1] - 1 > 0 && arr[cloud[0] + 1][cloud[1] - 1] > 0) + count++; + if (cloud[0] - 1 > 0 && cloud[1] + 1 < n + 1 && arr[cloud[0] - 1][cloud[1] + 1] > 0) + count++; + if (cloud[0] + 1 < n + 1 && cloud[1] + 1 < n + 1 && arr[cloud[0] + 1][cloud[1] + 1] > 0) + count++; + + temp.add(new int[]{cloud[0], cloud[1], count}); + } + + // 물의 양 증가 + for (int[] element : temp) { + arr[element[0]][element[1]] += element[2]; + } + } + + // 새로운 구름 생성 + private static void makeClouds() { + boolean[][] visited = new boolean[n + 1][n + 1]; // 이전 구름 위치 체크 + List temp = new ArrayList<>(); + + // 이전 구름 위치 표시 + for (int[] cloud : clouds) { + visited[cloud[0]][cloud[1]] = true; + } + + // 물의 양이 2 이상이고 구름이 사라진 칸이 아닌 곳에 구름 생성 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (!visited[i][j] && arr[i][j] > 1) { + temp.add(new int[]{i, j}); + arr[i][j] -= 2; + } + } + } + clouds = temp; + } + + // 격자에 있는 물의 양의 합 계산 + private static int sum() { + int result = 0; + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + result += arr[i][j]; + } + } + return result; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj15558.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj15558.java" new file mode 100644 index 00000000..03f88210 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj15558.java" @@ -0,0 +1,108 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.LinkedList; +import java.util.Queue; + +public class boj15558 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 이동 방향 배열 (아래, 위, 제자리) + private static final int[] dx = {1, -1, 0}; + private static final int[] dy = {0, 0, 0}; + + // 게임 맵과 방문 여부 체크 배열 + private static int[][] map; + private static boolean[][] visited; + + // 맵의 길이와 점프 거리 + private static int n, k; + + public static void main(String args[]) throws IOException { + setting(); // 초기 설정 + + // BFS 탐색 결과에 따라 도달 가능 여부 출력 + if (bfs(0, 0)) { + sb.append(1); + } else { + sb.append(0); + } + + // 결과 출력 + bw.write(sb.toString() + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 설정을 처리하는 메소드 + private static void setting() throws IOException { + // 맵의 길이와 점프 거리 입력 + String[] input = br.readLine().split(" "); + n = Integer.parseInt(input[0]); + k = Integer.parseInt(input[1]); + + // 맵과 방문 배열 초기화 + map = new int[n][2]; + visited = new boolean[n][2]; + + // 첫 번째 줄 입력 + input = br.readLine().split(""); + for (int i = 0; i < n; i++) { + map[i][0] = Integer.parseInt(input[i]); + } + + // 두 번째 줄 입력 + input = br.readLine().split(""); + for (int i = 0; i < n; i++) { + map[i][1] = Integer.parseInt(input[i]); + } + } + + // BFS 탐색을 수행하는 메소드 + private static boolean bfs(int x, int y) { + Queue queue = new LinkedList<>(); + visited[x][y] = true; + queue.add(new int[] {x, y, 0}); // x좌표, y좌표, 시간 + + while (!queue.isEmpty()) { + int[] current = queue.poll(); + + // 세 가지 이동 방식에 대해 탐색 + for (int i = 0; i < 3; i++) { + int nx = current[0]; + int ny = current[1]; + + // 일반 이동 + if (i != 2) { + nx += dx[i]; + ny += dy[i]; + } + // k만큼 점프하며 라인 변경 + else { + nx += k; + ny = ny == 0 ? 1 : 0; + } + + // 끝에 도달했다면 성공 + if (nx >= n) { + return true; + } + + // 유효하지 않은 이동이면 스킵 + if (nx < 0 || nx < current[2] + 1 || map[nx][ny] == 0 || visited[nx][ny]) continue; + + // 방문 처리 후 큐에 추가 + visited[nx][ny] = true; + queue.add(new int[]{nx, ny, current[2] + 1}); + } + } + + return false; // 도달 불가능 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj1953.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj1953.java" new file mode 100644 index 00000000..b2148e02 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj1953.java" @@ -0,0 +1,100 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class boj1953 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 적대 관계를 저장하는 인접 리스트 + private static List> table = new ArrayList<>(); + // 각 팀의 구성원을 저장하는 리스트 + private static List team1 = new ArrayList<>(); + private static List team2 = new ArrayList<>(); + // 방문 여부를 체크하는 배열 + private static boolean[] visited; + // 전체 인원 수 + private static int n; + + public static void main(String[] args) throws IOException{ + setting(); // 초기 데이터 설정 + + // 모든 사람에 대해 팀 배정 + for (int i = 1; i <= n; i++) { + if (!visited[i]) { + dfs(i, 1); // 방문하지 않은 사람부터 DFS 시작 + } + } + + // team1 결과 출력 준비 + sb.append(team1.size()).append("\n"); + Collections.sort(team1); + Collections.sort(team2); + for (int person : team1) { + sb.append(person).append(" "); + } + sb.deleteCharAt(sb.length() - 1); + sb.append("\n"); + + // team2 결과 출력 준비 + sb.append(team2.size()).append("\n"); + for (int person : team2) { + sb.append(person).append(" "); + } + sb.deleteCharAt(sb.length() - 1); + sb.append("\n"); + + // 결과 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void setting() throws IOException { + n = Integer.parseInt(br.readLine()); + // 인접 리스트 초기화 + for (int i = 0; i <= n; i++) { + table.add(new ArrayList<>()); + } + visited = new boolean[n + 1]; + + // 적대 관계 입력 받기 + for (int i = 0; i < n; i++) { + String[] input = br.readLine().split(" "); + int count = Integer.parseInt(input[0]); + for (int j = 1 ; j <= count; j++) { + // 양방향 그래프로 저장 + table.get(i + 1).add(Integer.parseInt(input[j])); + table.get(Integer.parseInt(input[j])).add(i + 1); + } + } + } + + // DFS로 팀을 나누는 메소드 + private static void dfs(int person, int teamNum) { + visited[person] = true; // 방문 처리 + + // 팀 배정 + if (teamNum == 1) { + team1.add(person); + } else { + team2.add(person); + } + + // 적대 관계에 있는 사람들을 반대 팀으로 배정 + for (int enemy : table.get(person)) { + if (!visited[enemy]) { + dfs(enemy, teamNum * -1); // 팀 번호를 반대로 바꿔서 재귀 호출 + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_1.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_1.java" new file mode 100644 index 00000000..d1d10cd4 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_1.java" @@ -0,0 +1,71 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class boj2229_1 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + private static int n; // 학생 수 + private static int[] students; // 학생들의 키를 저장하는 배열 + private static int[] dp; // dp를 위한 배열 + private static int[][] maxValues; // 구간별 최대값을 저장하는 2차원 배열 + private static int[][] minValues; // 구간별 최소값을 저장하는 2차원 배열 + + public static void main(String[] args) throws IOException { + setting(); // 초기 데이터 설정 + preprocess(); // 구간별 최대/최소값 미리 계산 + dp(); // dp로 최대 점수 계산 + + // 결과 출력 + sb.append(dp[n]); + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void setting() throws IOException { + n = Integer.parseInt(br.readLine()); + students = new int[n + 1]; + dp = new int[n + 1]; + maxValues = new int[n + 1][n + 1]; + minValues = new int[n + 1][n + 1]; + + // 학생들의 키 입력 받기 + String[] input = br.readLine().split(" "); + for (int i = 1; i <= n; i++) { + students[i] = Integer.parseInt(input[i - 1]); + } + } + + // 구간별 최대값과 최소값을 미리 계산하는 메소드 + private static void preprocess() { + for (int i = 1; i <= n; i++) { + // 구간의 길이가 1인 경우 초기화 + maxValues[i][i] = students[i]; + minValues[i][i] = students[i]; + // 구간의 길이를 늘려가며 최대/최소값 계산 + for (int j = i + 1; j <= n; j++) { + maxValues[i][j] = Math.max(maxValues[i][j-1], students[j]); + minValues[i][j] = Math.min(minValues[i][j-1], students[j]); + } + } + } + + // dp로 최대 점수를 계산하는 메소드 + private static void dp() { + for (int i = 1; i <= n; i++) { + // 각 위치에서 가능한 모든 구간을 고려 + for (int j = 1; j <= i; j++) { + // 현재 구간의 점수와 이전까지의 최대 점수를 합한 값 중 최대값 선택 + dp[i] = Math.max(dp[i - j] + (maxValues[i-j+1][i] - minValues[i-j+1][i]), dp[i]); + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_2.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_2.java" new file mode 100644 index 00000000..1b7a9b6f --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj2229_2.java" @@ -0,0 +1,66 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +public class boj2229_2 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + private static int n; // 학생 수 + private static int[] students; // 학생들의 키를 저장하는 배열 + private static int[] dp; // dp를 위한 배열 + + public static void main(String[] args) throws IOException{ + setting(); // 초기 데이터 설정 + dp(); // dp로 최대 점수 계산 + + // 결과 출력 + sb.append(dp[n]); + + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void setting() throws IOException { + n = Integer.parseInt(br.readLine()); + students = new int[n + 1]; + dp = new int[n + 1]; + + // 학생들의 키 입력 받기 + String[] input = br.readLine().split(" "); + for (int i = 1; i <= n; i++) { + students[i] = Integer.parseInt(input[i - 1]); + } + } + + // dp로 최대 점수를 계산하는 메소드 + private static void dp() { + for (int i = 1; i <= n; i++) { + // 각 위치에서 가능한 모든 구간을 고려 + for (int j = 1; j <= i; j++) { + // 현재 구간의 점수와 이전까지의 최대 점수를 합한 값 중 최대값 선택 + dp[i] = Math.max(dp[i - j] + degree(i, j), dp[i]); + } + } + } + + // 특정 구간의 최대 키와 최소 키의 차이를 계산하는 메소드 + private static int degree(int i, int j) { + int min = 10004; // 최소값 초기화 (문제의 조건에 따른 최대값보다 큰 수) + int max = -1; // 최대값 초기화 + // i-j+1부터 i까지의 구간에서 최대값과 최소값 찾기 + for (int k = i-j+1; k <= i; k++) { + min = Math.min(min, students[k]); + max = Math.max(max, students[k]); + } + + return max - min; // 최대값과 최소값의 차이 반환 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj23843.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj23843.java" new file mode 100644 index 00000000..ae17f33b --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj23843.java" @@ -0,0 +1,88 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Collections; + +public class boj23843 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // n: 작업의 개수, m: 처리할 수 있는 라인의 수 + private static int n, m; + // 작업 시간들을 저장할 리스트 + private static List list = new ArrayList<>(); + // 각 라인별 작업 시간의 합을 저장할 배열 + private static int[] sum; + + public static void main(String args[]) throws IOException { + // 첫 줄에서 n과 m을 입력받음 + String[] input = br.readLine().split(" "); + n = Integer.parseInt(input[0]); + m = Integer.parseInt(input[1]); + sum = new int[m]; + + // sum 배열을 0으로 초기화 + Arrays.fill(sum, 0); + + // 작업 시간들을 입력받아 리스트에 저장 + input = br.readLine().split(" "); + for (String element : input) { + list.add(Integer.parseInt(element)); + } + // 작업 시간을 내림차순으로 정렬 + Collections.sort(list, Collections.reverseOrder()); + + // 각 작업에 대해 function 메소드 호출 + for (int i : list) { + function(i); + } + + // 최종 결과를 구하기 위해 정렬 + Arrays.sort(sum); + + // 가장 큰 값(마지막 작업이 끝나는 시간)을 출력 + bw.write(sum[sum.length - 1] + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 각 작업을 라인에 배정하는 함수 + private static void function(int t) { + // sum 배열을 정렬하여 가장 작은 값을 가진 라인에 현재 작업 배정 + Arrays.sort(sum); + sum[0] += t; + + // minIndex() 함수 사용시 코드 +// int index = minIndex(); +// sum[index] += t; + } + + // 현재 가장 적은 작업량을 가진 라인의 인덱스를 찾는 함수 + private static int minIndex() { + int index = 0; + int min = sum[index]; + + // 비어있는 라인이 있다면 그 라인의 인덱스 반환 + for (int i = 0; i < m; i++) { + if (sum[i] == 0) { + return i; + } + + // 현재까지의 최소값보다 작은 값을 발견하면 갱신 + if (sum[i] < min) { + min = sum[i]; + index = i; + } + } + + return index; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj4577.java" "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj4577.java" new file mode 100644 index 00000000..31c2d76f --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:3\354\243\274\354\260\250/boj4577.java" @@ -0,0 +1,181 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; + +public class boj4577 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 상하좌우 이동을 위한 방향 배열 + private static final int[] dx = {1, -1, 0, 0}; // 하, 상, 우, 좌 + private static final int[] dy = {0, 0, 1, -1}; + + // 목표지점(+)들의 좌표를 저장하는 리스트 + private static List spots; + // 게임 맵 + private static char[][] map; + // 캐릭터의 현재 위치 + private static int[] hero = new int[2]; + // 이동 명령어들 + private static char[] commands; + // 맵의 크기 + private static int r, c; + + public static void main(String[] args) throws IOException { + // 테스트 케이스 반복 + for (int i = 1; ; i++) { + String[] input = br.readLine().split(" "); + r = Integer.parseInt(input[0]); + c = Integer.parseInt(input[1]); + + // 0 0이 입력되면 종료 + if (r == 0 && c == 0) break; + + // 게임 맵과 명령어 입력 받기 + setting(); + sb.append(String.format("Game %d: ", i)); + + // 게임 실행 및 결과 저장 + if (play()) { + sb.append("complete\n"); + } else { + sb.append("incomplete\n"); + } + + // 최종 맵 상태 출력 + print(); + } + + // 결과 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 게임 맵과 초기 상태 설정 + private static void setting() throws IOException { + map = new char[r][c]; + spots = new ArrayList<>(); + + // 맵 정보 입력 받기 + for (int i = 0; i < r; i++) { + char[] input = br.readLine().toCharArray(); + for (int j = 0; j < c; j++) { + map[i][j] = input[j]; + // 캐릭터 위치 저장 + if (map[i][j] == 'w' || map[i][j] == 'W') { + hero[0] = i; + hero[1] = j; + } + // 목표지점 위치 저장 + if (map[i][j] == '+' || map[i][j] == 'W' || map[i][j] == 'B') { + spots.add(new int[]{i, j}); + } + } + } + // 이동 명령어 입력 받기 + commands = br.readLine().toCharArray(); + } + + // 게임 실행 + private static boolean play() { + for (char command : commands) { + move(getDirection(command)); + if (finish()) { // 모든 박스가 목표지점에 도달했는지 확인 + return true; + } + } + return false; + } + + // 명령어에 따른 방향 반환 + private static int getDirection(char c) { + if (c == 'U') return 1; // 상 + if (c == 'D') return 0; // 하 + if (c == 'R') return 2; // 우 + return 3; // 좌 + } + + // 캐릭터 이동 처리 + private static void move(int dir) { + int nx = hero[0] + dx[dir]; + int ny = hero[1] + dy[dir]; + + // 이동 불가능한 경우 리턴 + if (OOB(nx, ny) || blocked(nx, ny, dir)) return; + + // 박스를 밀어야 하는 경우 + if (map[nx][ny] == 'b' || map[nx][ny] == 'B') { + int nxB = nx + dx[dir]; + int nyB = ny + dy[dir]; + map[nxB][nyB] = isSpot(nxB, nyB) ? 'B' : 'b'; + } + + // 캐릭터 이동 처리 + map[nx][ny] = isSpot(nx, ny) ? 'W' : 'w'; + map[hero[0]][hero[1]] = isSpot(hero[0], hero[1]) ? '+' : '.'; + hero[0] = nx; + hero[1] = ny; + } + + // 맵 범위 체크 + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > r - 1 || ny < 0 || ny > c - 1; + } + + // 이동 가능 여부 체크 + private static boolean blocked(int nx, int ny, int dir) { + if (map[nx][ny] == '#') return true; + + if (map[nx][ny] == 'b' || map[nx][ny] == 'B') { + int nxB = nx + dx[dir]; + int nyB = ny + dy[dir]; + return OOB(nxB, nyB) || map[nxB][nyB] == '#' || + map[nxB][nyB] == 'b' || map[nxB][nyB] == 'B'; + } + return false; + } + + // 목표지점 여부 체크 + private static boolean isSpot(int x, int y) { + for (int[] spot : spots) { + if (spot[0] == x && spot[1] == y) return true; + } + return false; + } + + // 게임 클리어 여부 체크 + private static boolean finish() { + for (int[] spot : spots) { + char c = map[spot[0]][spot[1]]; + if (c != 'B' && c != 'b') return false; + } + return true; + } + + // 최종 맵 상태 출력 + private static void print() { + // 목표지점 표시 업데이트 + for (int[] spot : spots) { + char c = map[spot[0]][spot[1]]; + if (c == 'b') map[spot[0]][spot[1]] = 'B'; + else if (c == 'w') map[spot[0]][spot[1]] = 'W'; + else if (c == '.') map[spot[0]][spot[1]] = '+'; + } + + // 맵 출력 + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + sb.append(map[i][j]); + } + sb.append("\n"); + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_1.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_1.java" new file mode 100644 index 00000000..2f2c2769 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_1.java" @@ -0,0 +1,87 @@ +import java.io.*; +public class boj14238_1 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 최종 결과를 저장할 StringBuilder + private static final StringBuilder answer = new StringBuilder(); + // A, B, C 각각의 개수를 저장할 배열 + private static int[] countWorkDay; + // 전체 문자열의 길이 + private static int count = 0; + // 유효한 답을 찾았는지 확인하는 플래그 + private static boolean flag = false; + + public static void main(String[] args) throws IOException { + // 초기 입력 처리 + init(); + // DFS를 위한 StringBuilder 생성 + StringBuilder sb = new StringBuilder(); + // DFS 수행 + dfs(sb); + // 유효한 답이 없는 경우 -1 출력 + if (answer.length() == 0) { + answer.append("-1"); + } + // 결과 출력 및 스트림 닫기 + bw.write(answer.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 입력을 처리하는 메소드 + private static void init() throws IOException { + String input = br.readLine(); + // A, B, C의 개수를 저장할 배열 초기화 + countWorkDay = new int[3]; + // 입력된 문자열을 순회하며 각 문자의 개수 카운트 + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == 'A') countWorkDay[0]++; + else if (c == 'B') countWorkDay[1]++; + else countWorkDay[2]++; + count++; + } + } + + // DFS로 가능한 문자열을 만드는 메소드 + private static void dfs(StringBuilder sb) { + // 이미 답을 찾은 경우 종료 + if (flag) return; + // 문자열이 완성된 경우 + if (sb.length() == count) { + answer.append(sb.toString()); + flag = true; + return; + } + + int len = sb.length(); + // A는 제약 없이 추가 가능 + if (countWorkDay[0] > 0) { + sb.append('A'); + countWorkDay[0]--; + dfs(sb); + countWorkDay[0]++; + sb.deleteCharAt(len); + } + // B는 이전 문자가 B가 아닌 경우에만 추가 가능 + if (countWorkDay[1] > 0 && (len == 0 || sb.charAt(len - 1) != 'B')) { + sb.append('B'); + countWorkDay[1]--; + dfs(sb); + countWorkDay[1]++; + sb.deleteCharAt(len); + } + // C는 이전 문자와 그 이전 문자가 C가 아닌 경우에만 추가 가능 + if (countWorkDay[2] > 0 && + (len == 0 || sb.charAt(len - 1) != 'C') && + (len <= 1 || sb.charAt(len - 2) != 'C')) { + sb.append('C'); + countWorkDay[2]--; + dfs(sb); + countWorkDay[2]++; + sb.deleteCharAt(len); + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_2.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_2.java" new file mode 100644 index 00000000..6d164be5 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj14238_2.java" @@ -0,0 +1,90 @@ +import java.io.*; +public class boj14238_2 { + // 입출력을 위한 BufferedReader와 BufferedWriter + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 결과 문자열을 만들기 위한 StringBuilder + private static final StringBuilder sb = new StringBuilder(); + // dp[a][b][c][prev1][prev2]: A,B,C 각각 a,b,c개 남았고 이전 문자가 prev1, 그 이전이 prev2일 때 가능 여부 + private static int[][][][][] dp; + // A,B,C 각각의 개수를 저장하는 배열 + private static int[] countWorkDay; + // 전체 문자열의 길이 + private static int n = 0; + + public static void main(String[] args) throws IOException { + // 초기 입력 처리 + init(); + // dp 함수 호출하여 결과 확인 + if (dp(countWorkDay[0], countWorkDay[1], countWorkDay[2], 0, 0)) { + bw.write(sb.toString()); + } else { + bw.write("-1"); + } + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 입력을 처리하는 메소드 + private static void init() throws IOException { + String input = br.readLine(); + n = input.length(); + // A,B,C 개수 카운트 + countWorkDay = new int[3]; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == 'A') countWorkDay[0]++; + else if (c == 'B') countWorkDay[1]++; + else countWorkDay[2]++; + } + // DP 배열 초기화 (-1로 초기화하여 방문하지 않은 상태 표시) + dp = new int[countWorkDay[0]+1][countWorkDay[1]+1][countWorkDay[2]+1][3][3]; + for (int i = 0; i <= countWorkDay[0]; i++) + for (int j = 0; j <= countWorkDay[1]; j++) + for (int k = 0; k <= countWorkDay[2]; k++) + for (int l = 0; l < 3; l++) + for (int m = 0; m < 3; m++) + dp[i][j][k][l][m] = -1; + } + + // DP를 이용한 문자열 생성 메소드 + private static boolean dp(int a, int b, int c, int prev1, int prev2) { + // 문자열이 완성된 경우 + if (sb.length() == n) return true; + // 이미 계산된 경우 결과 반환 + if (dp[a][b][c][prev1][prev2] != -1) + return dp[a][b][c][prev1][prev2] == 1; + + // A를 추가할 수 있는 경우 (제약 없음) + if (a > 0) { + sb.append('A'); + if (dp(a-1, b, c, 0, prev1)) { + dp[a][b][c][prev1][prev2] = 1; + return true; + } + sb.deleteCharAt(sb.length() - 1); + } + // B를 추가할 수 있는 경우 (이전 문자가 B가 아니어야 함) + if (b > 0 && prev1 != 1) { + sb.append('B'); + if (dp(a, b-1, c, 1, prev1)) { + dp[a][b][c][prev1][prev2] = 1; + return true; + } + sb.deleteCharAt(sb.length() - 1); + } + // C를 추가할 수 있는 경우 (이전 두 문자가 C가 아니어야 함) + if (c > 0 && prev1 != 2 && prev2 != 2) { + sb.append('C'); + if (dp(a, b, c-1, 2, prev1)) { + dp[a][b][c][prev1][prev2] = 1; + return true; + } + sb.deleteCharAt(sb.length() - 1); + } + // 현재 상태에서 불가능한 경우 + dp[a][b][c][prev1][prev2] = 0; + return false; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj16498.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj16498.java" new file mode 100644 index 00000000..ddacafd4 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj16498.java" @@ -0,0 +1,103 @@ +import java.io.*; +import java.util.*; +public class boj16498 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + // 세 개의 배열을 저장할 전역 변수 선언 + private static int[] a; + private static int[] b; + private static int[] c; + + public static void main(String[] args) throws IOException { + // 입력값 설정 + setting(); + + // 세 배열에서 최소 차이를 계산 + int min = min(a, b, c); + + // 결과를 StringBuilder에 추가 + sb.append(min).append("\n"); + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력값을 받아 배열을 초기화하는 메소드 + private static void setting() throws IOException { + // 첫 번째 줄에서 세 배열의 크기를 입력받음 + String[] input = br.readLine().split(" "); + + // 입력받은 크기로 배열 초기화 + a = new int[Integer.parseInt(input[0])]; + b = new int[Integer.parseInt(input[1])]; + c = new int[Integer.parseInt(input[2])]; + + // a 배열의 원소들을 입력받음 + input = br.readLine().split(" "); + for (int i = 0; i < a.length; i++) { + a[i] = Integer.parseInt(input[i]); + } + + // b 배열의 원소들을 입력받음 + input = br.readLine().split(" "); + for (int i = 0; i < b.length; i++) { + b[i] = Integer.parseInt(input[i]); + } + + // c 배열의 원소들을 입력받음 + input = br.readLine().split(" "); + for (int i = 0; i < c.length; i++) { + c[i] = Integer.parseInt(input[i]); + } + } + + // 세 배열에서 최소 차이를 찾는 메소드 + private static int min(int[] a, int[] b, int[] c) { + // c 배열을 정렬하여 이진 탐색 준비 + Arrays.sort(c); + // 결과값을 저장할 변수를 최대값으로 초기화 + int result = Integer.MAX_VALUE; + + // a, b 배열의 모든 조합에 대해 반복 + for (int i = 0; i < a.length; i++) { + for (int j = 0; j < b.length; j++) { + // 현재 a[i]와 b[j] 중 최대값과 최소값 계산 + int max = Math.max(a[i], b[j]); + int min = Math.min(a[i], b[j]); + + // c 배열에서 min값 이상인 첫 번째 위치를 찾음 + int minIndex = Arrays.binarySearch(c, min); + if (minIndex < 0) { + minIndex = -(minIndex + 1); // min보다 큰 첫 번째 값의 index + if (minIndex >= c.length) { // 그런 값이 없다면 + minIndex = c.length - 1; // min보다 작은 값들 중 가장 큰 값의 index + } + } + + // c 배열에서 max값 이하인 마지막 위치를 찾음 + int maxIndex = Arrays.binarySearch(c, max); + if (maxIndex < 0) { + maxIndex = -(maxIndex + 1); // max보다 작은 마지막 값의 index + if (maxIndex < 0) { // 그런 값이 없다면 + maxIndex = 0; // max보다 큰 값들 중 가장 작은 값의 index + } + } + + // 찾은 범위의 전후 값까지 포함하여 최소 차이 계산 + for (int k = Math.max(0, minIndex-1); k <= Math.min(c.length-1, maxIndex+1); k++) { + // 현재 조합에서의 최대값과 최소값의 차이 계산 + int temp = Math.max(max, c[k]) - Math.min(min, c[k]); + // 전체 최소값 갱신 + result = Math.min(result, temp); + } + } + } + + return result; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj22944.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj22944.java" new file mode 100644 index 00000000..6e067b17 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj22944.java" @@ -0,0 +1,137 @@ +import java.io.*; +import java.util.*; +public class boj22944 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 상하좌우 이동을 위한 방향 배열 + private static final int[] dx = { 1, 0, -1, 0 }; + private static final int[] dy = { 0, 1, 0, -1 }; + + // 맵 정보와 방문 체크를 위한 배열들 + private static char[][] map; // 맵 정보 + private static boolean[][][] visited; // 방문 체크 (x, y, 우산 번호) + private static int[] start = new int[2]; // 시작 위치 + private static int[] end = new int[2]; // 도착 위치 + private static int n, h, d; // 맵 크기, 체력, 우산 내구도 + + public static void main(String[] args) throws IOException { + init(); // 초기 설정 + sb.append(bfs(start[0], start[1])); // BFS 실행 및 결과 저장 + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 설정을 위한 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 맵 크기 + h = Integer.parseInt(st.nextToken()); // 초기 체력 + d = Integer.parseInt(st.nextToken()); // 우산 내구도 + + map = new char[n][n]; + int umbrellaCount = 0; // 우산 개수 + + // 맵 정보 입력 + for (int i = 0; i < n; i++) { + String line = br.readLine(); + for (int j = 0; j < n; j++) { + char c = line.charAt(j); + if (c == 'U') { // 우산인 경우 번호 부여 + umbrellaCount++; + map[i][j] = (char) (umbrellaCount + '0'); + } else { + map[i][j] = c; + if (c == 'S') { // 시작 위치 저장 + start[0] = i; + start[1] = j; + } else if (c == 'E') { // 도착 위치 저장 + end[0] = i; + end[1] = j; + } + } + } + } + visited = new boolean[n][n][umbrellaCount + 1]; + } + + // BFS 탐색을 수행하는 메소드 + private static int bfs(int x, int y) { + Queue queue = new ArrayDeque<>(); + visited[x][y][0] = true; + map[x][y] = '.'; + queue.add(new Node(x, y, 0, h, 0, 0)); // 초기 위치 큐에 추가 + + while (!queue.isEmpty()) { + Node current = queue.poll(); + + if (current.hp == 0) continue; // 체력이 0이면 죽음 + + // 도착지점에 도달한 경우 + if (current.x == end[0] && current.y == end[1]) { + return current.time; + } + + // 4방향 탐색 + for (int i = 0; i < 4; i++) { + int nx = current.x + dx[i]; + int ny = current.y + dy[i]; + + // 맵을 벗어나거나 이미 방문한 경우 + if (OOB(nx, ny) || visited[nx][ny][current.umbrellaIndex]) continue; + + // 빈 공간인 경우 + if (map[nx][ny] == '.'){ + if (current.durability > 0) { // 우산 내구도가 남아있는 경우 + visited[nx][ny][current.umbrellaIndex] = true; + queue.add(new Node(nx, ny, current.time+1, current.hp, current.umbrellaIndex, current.durability-1)); + } else { // 우산이 없는 경우 체력 감소 + visited[nx][ny][current.umbrellaIndex] = true; + queue.add(new Node(nx, ny, current.time+1, current.hp-1, current.umbrellaIndex, 0)); + } + } + // 도착지점인 경우 + else if (map[nx][ny] == 'E') { + visited[nx][ny][current.umbrellaIndex] = true; + queue.add(new Node(nx, ny, current.time+1, current.hp, current.umbrellaIndex, current.durability)); + } + // 우산이 있는 경우 + else { + queue.add(new Node(nx, ny, current.time+1, current.hp, map[nx][ny] - '0', d-1)); + visited[nx][ny][map[nx][ny] - '0'] = true; + map[nx][ny] = '.'; // 우산 사용 후 제거 + } + } + } + return -1; // 도착지점에 도달할 수 없는 경우 + } + + // 맵 범위를 벗어났는지 체크하는 메소드 + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n-1 || ny < 0 || ny > n-1; + } + + // 현재 상태를 저장하는 Node 클래스 + static class Node { + private int x, y; // 현재 위치 + private int time; // 이동 시간 + private int hp; // 현재 체력 + private int umbrellaIndex; // 현재 우산 번호 + private int durability; // 우산 내구도 + + public Node(int x, int y, int time, int hp, int umbrellaIndex, int durability) { + this.x = x; + this.y = y; + this.time = time; + this.hp = hp; + this.umbrellaIndex = umbrellaIndex; + this.durability = durability; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj30804.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj30804.java" new file mode 100644 index 00000000..ea39730c --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj30804.java" @@ -0,0 +1,70 @@ +import java.io.*; +import java.util.*; +public class boj30804 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 과일의 개수 n + private static int n; + // 과일의 종류를 저장할 배열 + private static int[] fruits; + + public static void main(String[] args) throws IOException { + setting(); // 입력 받기 + + // 최대 길이 계산 및 출력 + sb.append(function()); + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력을 받아 초기 설정을 하는 메소드 + private static void setting() throws IOException { + // 과일의 개수 입력 + n = Integer.parseInt(br.readLine()); + fruits = new int[n]; + + // 각 과일의 종류 입력 + String[] input = br.readLine().split(" "); + for (int i = 0; i < n; i++) { + fruits[i] = Integer.parseInt(input[i]); + } + } + + // 최대 길이를 계산하는 메소드 (투 포인터 알고리즘) + private static int function() { + int max = 0; // 최대 길이를 저장할 변수 + int start = 0; // 시작 포인터 + int end = 0; // 끝 포인터 + + // 각 과일 종류의 개수를 저장할 HashMap + Map map = new HashMap<>(); + + // 끝 포인터가 배열 끝에 도달할 때까지 반복 + while (end < n) { + // 현재 과일 추가 + map.put(fruits[end], map.getOrDefault(fruits[end], 0) + 1); + + // 과일 종류가 2개를 초과하는 경우 + if (map.size() > 2) { + // 시작 포인터의 과일 개수 감소 + map.put(fruits[start], map.get(fruits[start])-1); + // 과일 개수가 0이 되면 맵에서 제거 + if (map.get(fruits[start]) == 0) { + map.remove(fruits[start]); + } + start++; // 시작 포인터 이동 + } + // 현재 구간의 길이와 최대 길이 비교 + max = Math.max(max, end-start+1); + end++; // 끝 포인터 이동 + } + return max; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9252.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9252.java" new file mode 100644 index 00000000..f6a27c52 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9252.java" @@ -0,0 +1,82 @@ +import java.io.*; + +public class boj9252 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 입력받은 두 문자열을 저장할 배열 + private static char[] input1; + private static char[] input2; + // LCS의 길이를 저장할 2차원 DP 배열 + private static int[][] dp; + + public static void main(String[] args) throws IOException { + setting(); // 입력 받기 + dp(); // LCS 길이 계산 + + // LCS의 길이 출력 + sb.append(dp[input1.length][input2.length]).append("\n"); + // LCS 문자열 출력 + sb.append(makeLCS()); + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력을 받아 초기 설정을 하는 메소드 + private static void setting() throws IOException { + // 두 문자열을 입력받아 char 배열로 변환 + input1 = br.readLine().toCharArray(); + input2 = br.readLine().toCharArray(); + + // DP 배열 초기화 (크기는 각 문자열 길이 + 1) + dp = new int[input1.length + 1][input2.length + 1]; + } + + // LCS의 길이를 계산하는 메소드 + private static void dp() { + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + // 현재 비교하는 문자가 같은 경우 + if (input1[i-1] == input2[j-1]) + dp[i][j] = dp[i-1][j-1] + 1; // 대각선 위 값 + 1 + // 다른 경우 위쪽과 왼쪽 값 중 큰 값 선택 + else + dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); + } + } + } + + // 실제 LCS 문자열을 구성하는 메소드 + private static String makeLCS() { + StringBuilder sb = new StringBuilder(); + // 배열의 가장 끝에서부터 시작 + int i = dp.length - 1; + int j = dp[0].length - 1; + + // 배열의 처음까지 역추적 + while (i != 0 && j != 0) { + // 두 문자가 같은 경우 + if (input1[i-1] == input2[j-1]) { + sb.append(input1[i-1]); // LCS에 문자 추가 + i--; + j--; + } + // 다른 경우 위쪽과 왼쪽 중 큰 값으로 이동 + else { + if (dp[i-1][j] > dp[i][j-1]) { + i--; + } else { + j--; + } + } + } + // 역순으로 구한 문자열을 뒤집어서 반환 + return sb.reverse().toString(); + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9440.java" "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9440.java" new file mode 100644 index 00000000..059c4e00 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:4\354\243\274\354\260\250/boj9440.java" @@ -0,0 +1,95 @@ +import java.io.*; + +public class boj9440 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 남은 숫자의 개수를 저장하는 변수 + private static int t; + // 각 숫자(0-9)의 등장 횟수를 저장하는 배열 + private static int[] trie; + + public static void main(String[] args) throws IOException { + // 입력이 0이 들어올 때까지 반복 + while (true) { + setting(); // 입력 받기 + if (t == 0) break; // 0이 입력되면 종료 + sb.append(greedy()).append("\n"); // 결과 계산 및 저장 + } + + // 결과 출력 및 스트림 닫기 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력을 받아 초기 설정을 하는 메소드 + private static void setting() throws IOException { + String[] input = br.readLine().split(" "); + int n = Integer.parseInt(input[0]); // 숫자의 개수 + t = n; // 남은 숫자 개수 초기화 + + // 각 숫자의 등장 횟수를 저장할 배열 초기화 + trie = new int[10]; + + if (t == 0) return; // 0이 입력되면 종료 + + // 입력받은 숫자들의 등장 횟수 카운트 + for (int i = 1; i <=n; i++) { + int num = Integer.parseInt(input[i]); + trie[num]++; + } + } + + // 가능한 가장 작은 합을 구하는 메소드 (그리디 알고리즘) + private static int greedy() throws IOException{ + // 두 수를 저장할 문자열 + String a = ""; + String b = ""; + + // 첫 번째 자리수 설정 (0이 아닌 수로) + for (int i = 1; i < 10;) { + if (a.length() > 0 && b.length() > 0) break; + + if (trie[i] > 0) { + // 첫 번째 수의 첫 자리 + if (a.length() == 0) { + a += i; + } + // 두 번째 수의 첫 자리 + else { + b += i; + } + trie[i]--; + t--; + } else { + i++; + } + } + + // 남은 숫자들을 두 수에 번갈아가며 추가 + int index = 0; + while (t > 0) { + if (trie[index] > 0) { + // 두 수의 길이가 같으면 첫 번째 수에 추가 + if (a.length() == b.length()) { + a += index; + } + // 다르면 두 번째 수에 추가 + else { + b += index; + } + trie[index]--; + t--; + } else { + index++; + } + } + + // 두 수의 합을 반환 + return Integer.parseInt(a) + Integer.parseInt(b); + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj12101.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj12101.java" new file mode 100644 index 00000000..28a5451a --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj12101.java" @@ -0,0 +1,80 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +public class boj12101 { + // 입출력을 위한 버퍼 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 결과 문자열을 만들기 위한 StringBuilder + private static final StringBuilder sb = new StringBuilder(); + + // 현재까지의 수열을 저장할 ArrayList + private static List list = new ArrayList<>(); + + // n: 만들어야 할 합, k: 찾아야 할 k번째 수열 + private static int n, k; + // 현재까지 찾은 수열의 개수 + private static int totalCount = 0; + // k번째 수열을 찾았는지 표시하는 플래그 + private static boolean flag = false; + + public static void main(String[] args) throws IOException { + // 입력값 처리 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 목표 합 n + k = Integer.parseInt(st.nextToken()); // 찾을 순서 k + + // DFS 시작 (초기 합은 0) + dfs(0); + + // k번째 수열을 찾지 못한 경우 + if (!flag) { + sb.append("-1"); + } + + // 결과 출력 및 버퍼 정리 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * DFS를 통해 1, 2, 3의 합으로 n을 만드는 모든 경우를 탐색 + * @param sum 현재까지의 합계 + */ + private static void dfs(int sum) { + // k번째 수열을 이미 찾았거나 현재 합이 n을 초과한 경우 탐색 중단(가지치기) + if (flag || sum > n) return; + + // 현재 합이 목표값 n과 같은 경우 + if (sum == n) { + totalCount++; // 찾은 수열 개수 증가 + + // 현재 수열이 k번째인 경우 + if (totalCount == k) { + // 현재 수열을 문자열로 변환 + for (int element : list) { + sb.append(element).append("+"); + } + sb.setLength(sb.length()-1); // 마지막 + 기호 제거 + sb.append("\n"); + flag = true; // k번째 수열 찾음을 표시 + } + return; + } + + // 1, 2, 3을 각각 시도 + for (int i = 1; i < 4; i++) { + list.add(i); // 현재 숫자 추가 + dfs(sum + i); // 다음 숫자 탐색 (재귀 호출) + list.remove(list.size()-1); // 백트래킹 (현재 숫자 제거) + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16401.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16401.java" new file mode 100644 index 00000000..301459c2 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16401.java" @@ -0,0 +1,65 @@ +import java.io.*; +import java.util.StringTokenizer; + +public class boj16401 { + // 입출력을 위한 BufferedReader와 BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 과자의 길이를 저장할 배열과 필요한 조각 수(m), 과자의 개수(n) + private static int[] snacks; + private static int m, n; + + public static void main(String[] args) throws IOException{ + init(); // 입력 받기 + int answer = binarySearch(); // 이진 탐색으로 최대 과자 길이 찾기 + + // 결과 출력 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력을 받는 초기화 함수 + private static void init() throws IOException{ + StringTokenizer st = new StringTokenizer(br.readLine()); + m = Integer.parseInt(st.nextToken()); // 필요한 과자 조각의 수 + n = Integer.parseInt(st.nextToken()); // 과자의 개수 + + snacks = new int[n]; // 과자 길이를 저장할 배열 초기화 + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < n; i++) { + snacks[i] = Integer.parseInt(st.nextToken()); // 각 과자의 길이 입력 + } + } + + // 이진 탐색으로 가능한 최대 과자 길이를 찾는 함수 + private static int binarySearch() { + int left = 1; // 최소 길이 + int right = 1000000000; // 최대 길이 (10^9) + int result = 0; // 결과값 저장 + + while (left <= right) { + int mid = left + (right - left) / 2; // 중간값 계산 (오버플로우 방지를 위해 이렇게 계산) + + if (canDistribute(mid)) { // mid 길이로 m개 이상의 조각을 만들 수 있는지 확인 + result = mid; // 가능하다면 결과 갱신 + left = mid + 1; // 더 큰 길이 탐색 + } else { + right = mid - 1; // 불가능하다면 더 작은 길이 탐색 + } + } + return result; + } + + // 주어진 길이(length)로 m개 이상의 과자 조각을 만들 수 있는지 확인하는 함수 + private static boolean canDistribute(int length) { + int count = 0; // 만들 수 있는 과자 조각의 수 + for (int snack : snacks) { + count += snack / length; // 각 과자를 length 길이로 나눴을 때 나오는 조각의 수 더하기 + if (count >= m) return true; // m개 이상의 조각을 만들 수 있다면 true 반환 + } + return false; // m개 이상의 조각을 만들 수 없다면 false 반환 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16964.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16964.java" new file mode 100644 index 00000000..2b669b30 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16964.java" @@ -0,0 +1,92 @@ +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringTokenizer; + +public class boj16964 { + // 입출력을 위한 버퍼 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 트리를 표현할 인접 리스트 + private static List> graph = new ArrayList<>(); + // 주어진 순서를 저장할 배열 + private static int[] givenOrder; + // DFS 방문 체크 배열 + private static boolean[] visited; + // 노드의 개수와 현재 확인 중인 순서의 인덱스 + private static int n, index; + + public static void main(String[] args) throws IOException{ + init(); // 초기화 + if (dfs(1)) { // 루트 노드(1)부터 DFS 시작 + bw.write(1 + "\n"); // 가능한 순서면 1 출력 + } else { + bw.write(0 + "\n"); // 불가능한 순서면 0 출력 + } + bw.flush(); + bw.close(); + br.close(); + } + + private static void init() throws IOException{ + // 노드의 개수 입력 + n = Integer.parseInt(br.readLine()); + index = 1; // DFS 순서 체크를 위한 인덱스 초기화 + + // 그래프 초기화 (0번 인덱스는 사용하지 않음) + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + + // 배열들 초기화 + givenOrder = new int[n + 1]; + visited = new boolean[n + 1]; + + // 트리의 간선 정보 입력 받기 + for (int i = 0; i < n - 1; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int node1 = Integer.parseInt(st.nextToken()); + int node2 = Integer.parseInt(st.nextToken()); + // 양방향 간선 추가 + graph.get(node1).add(node2); + graph.get(node2).add(node1); + } + + // 주어진 순서 입력 받기 + StringTokenizer st = new StringTokenizer(br.readLine()); + for (int i = 1; i <= n; i++) { + givenOrder[i] = Integer.parseInt(st.nextToken()); + } + + // order 배열 생성 (givenOrder의 역배열) + int[] order = new int[n + 1]; + for (int i = 1; i <= n; i++) { + order[givenOrder[i]] = i; // 각 노드가 몇 번째 순서인지 저장 + } + + // 각 노드의 인접 리스트를 주어진 순서(givenOrder)에 맞게 정렬 + for (int i = 1; i <= n; i++) { + Collections.sort(graph.get(i), (a, b) -> order[a] - order[b]); + } + } + + private static boolean dfs(int node) { + // 현재 방문한 노드가 주어진 순서와 다르면 false + if (givenOrder[index] != node) { + return false; + } + + index++; // 다음 순서 확인을 위해 인덱스 증가 + visited[node] = true; // 현재 노드 방문 처리 + + // 인접한 노드들을 방문 + for (int next : graph.get(node)) { + if (!visited[next]) { // 아직 방문하지 않은 노드만 방문 + if (!dfs(next)) return false; // 하나라도 순서가 맞지 않으면 false + } + } + return true; // 모든 순서가 맞으면 true + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16973.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16973.java" new file mode 100644 index 00000000..f066406d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj16973.java" @@ -0,0 +1,144 @@ +import java.util.*; +import java.io.*; + +public class boj16973 { + // 입출력을 위한 버퍼 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 4방향 이동을 위한 배열 (우, 하, 좌, 상) + private static final int[] dx = {1, 0, -1, 0}; + private static final int[] dy = {0, 1, 0, -1}; + + // 맵의 크기(n,m), 직사각형의 크기(h,w) + private static int n, m, h, w; + // 시작점과 도착점의 좌표 + private static int[] start = new int[2]; + private static int[] end = new int[2]; + // 맵 정보와 방문 여부를 저장하는 배열 + private static int[][] map; + private static boolean[][] visited; + + public static void main(String[] args) throws IOException{ + init(); // 입력 처리 및 초기화 + bfs(start[0], start[1]); // BFS 탐색 수행 + + // 도착점에 도달할 수 없으면 -1, 가능하면 최단 거리 출력 + if (map[end[0]][end[1]] == 0) { + sb.append(-1); + } else { + sb.append(map[end[0]][end[1]]); + } + + // 결과 출력 및 버퍼 정리 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 처리하고 초기 상태를 설정하는 함수 + * 시간복잡도: O(N * M) + */ + private static void init() throws IOException { + // 맵의 크기 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + + // 맵과 방문 배열 초기화 + map = new int[n][m]; + visited = new boolean[n][m]; + + // 맵 정보 입력 (1은 -1로 변환하여 장애물 표시) + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < m; j++) { + map[i][j] = Integer.parseInt(st.nextToken()); + if (map[i][j] == 1) map[i][j] = -1; + } + } + + // 직사각형의 크기와 시작점, 도착점 입력 + st = new StringTokenizer(br.readLine()); + h = Integer.parseInt(st.nextToken()); + w = Integer.parseInt(st.nextToken()); + start[0] = Integer.parseInt(st.nextToken()) - 1; + start[1] = Integer.parseInt(st.nextToken()) - 1; + end[0] = Integer.parseInt(st.nextToken()) - 1; + end[1] = Integer.parseInt(st.nextToken()) - 1; + } + + /** + * BFS로 최단 경로를 찾는 함수 + * 시간복잡도: O(N * M * (h + w)) + */ + private static void bfs(int x, int y) { + Queue q = new ArrayDeque<>(); + visited[x][y] = true; + q.add(new int[]{x, y}); + + while (!q.isEmpty()) { + int[] current = q.poll(); + + // 도착점에 도달한 경우 종료 + if (current[0] == end[0] && current[1] == end[1]) return; + + // 4방향 탐색 + for (int i = 0; i < 4; i++) { + int nx = current[0] + dx[i]; + int ny = current[1] + dy[i]; + + // 맵을 벗어나거나, 장애물이 있거나, 이미 방문한 경우 스킵 + if (OOB(nx, ny) || isBlocked(nx, ny) || visited[nx][ny]) continue; + + visited[nx][ny] = true; + map[nx][ny] = map[current[0]][current[1]] + 1; // 이동 거리 갱신 + q.add(new int[]{nx, ny}); + } + } + } + + /** + * 직사각형이 장애물과 겹치는지 확인하는 함수 + * 시간복잡도: O(h + w) + */ + private static boolean isBlocked(int nx, int ny) { + // 직사각형의 세로 변에 장애물이 있는지 확인 + for (int i = nx; i < nx+h; i++) { + if (map[i][ny] == -1 || map[i][ny+w-1] == -1) return true; + } + + // 직사각형의 가로 변에 장애물이 있는지 확인 + for (int i = ny; i < ny+w; i++) { + if (map[nx][i] == -1 || map[nx+h-1][i] == -1) return true; + } + + return false; + } + + /** + * 직사각형이 맵을 벗어나는지 확인하는 함수 + * 시간복잡도: O(1) + */ + private static boolean OOB(int nx, int ny) { + // 직사각형의 네 꼭짓점이 맵 안에 있는지 확인 + int nx2 = nx; + int ny2 = ny + w; + + int nx3 = nx + h; + int ny3 = ny; + + int nx4 = nx + h; + int ny4 = ny + w; + + if (nx < 0 || ny < 0) return true; + if (nx2 < 0 || ny2 > m) return true; + if (nx3 > n || ny3 < 0) return true; + if (nx4 > n || ny4 > m) return true; + + return false; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj17144.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj17144.java" new file mode 100644 index 00000000..b2937cb2 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj17144.java" @@ -0,0 +1,162 @@ +import java.io.*; +import java.util.*; + +public class boj17144 { + // 입출력을 위한 BufferedReader + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + + // 방의 행(r), 열(c), 시간(t) + private static int r, c, t; + + // 미세먼지 상태를 저장하는 2차원 배열 + private static int[][] room; + + // 공기청정기 객체 + private static final AirCleaner airCleaner = new AirCleaner(); + + // 상하좌우 이동을 위한 방향 배열 + private static final int[] dx = {1, 0, -1, 0}; + private static final int[] dy = {0, 1, 0, -1}; + + public static void main(String[] args) throws IOException { + init(); // 초기 입력 처리 + simulate(); // t초 동안 시뮬레이션 실행 + System.out.println(sum()); // 남은 미세먼지의 양 출력 + } + + // 초기 입력을 처리하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + r = Integer.parseInt(st.nextToken()); + c = Integer.parseInt(st.nextToken()); + t = Integer.parseInt(st.nextToken()); + + room = new int[r][c]; + + // 방의 상태와 공기청정기 위치 입력 + for (int i = 0; i < r; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < c; j++) { + room[i][j] = Integer.parseInt(st.nextToken()); + if (room[i][j] == -1) { + airCleaner.setCleaner(i, j); // 공기청정기 위치 저장 + } + } + } + } + + // t초 동안 시뮬레이션을 실행하는 메소드 + private static void simulate() { + for (int i = 0; i < t; i++) { + diffusion(); // 미세먼지 확산 + runCleaner(); // 공기청정기 작동 + } + } + + // 미세먼지 확산을 처리하는 메소드 + private static void diffusion() { + int[][] empty = new int[r][c]; // 확산되는 미세먼지를 임시 저장할 배열 + + // 모든 칸의 미세먼지 확산 처리 + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + int[] current = new int[]{i, j}; + int temp = room[current[0]][current[1]] / 5; // 확산되는 양 + + // 4방향으로 확산 시도 + for (int k = 0; k < 4; k++) { + int nx = current[0] + dx[k]; + int ny = current[1] + dy[k]; + + // 범위를 벗어나거나 공기청정기가 있으면 확산 불가 + if (isOutOfBounds(nx, ny) || isNearCleaner(nx, ny)) continue; + room[current[0]][current[1]] -= temp; // 현재 칸의 미세먼지 감소 + empty[nx][ny] += temp; // 확산되는 칸에 미세먼지 추가 + } + } + } + + // 확산된 미세먼지 반영 + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (empty[i][j] > 0) { + room[i][j] += empty[i][j]; + } + } + } + } + + // 공기청정기를 작동시키는 메소드 + private static void runCleaner() { + // 위쪽 공기청정기 순환 + for (int i = airCleaner.front[0] - 2; i > -1; i--) { + room[i + 1][0] = room[i][0]; + } + for (int i = 1; i < c; i++) { + room[0][i - 1] = room[0][i]; + } + for (int i = 1; i < airCleaner.front[0] + 1; i++) { + room[i - 1][c - 1] = room[i][c - 1]; + } + for (int i = c - 2; i > 0; i--) { + room[airCleaner.front[0]][i + 1] = room[airCleaner.front[0]][i]; + } + room[airCleaner.front[0]][1] = 0; + + // 아래쪽 공기청정기 순환 + for (int i = airCleaner.rear[0] + 2; i < r; i++) { + room[i - 1][0] = room[i][0]; + } + for (int i = 1; i < c; i++) { + room[r - 1][i - 1] = room[r - 1][i]; + } + for (int i = r - 2; i > airCleaner.rear[0] - 1; i--) { + room[i + 1][c - 1] = room[i][c - 1]; + } + for (int i = c - 2; i > 0; i--) { + room[airCleaner.rear[0]][i + 1] = room[airCleaner.rear[0]][i]; + } + room[airCleaner.rear[0]][1] = 0; + } + + // 좌표가 범위를 벗어났는지 확인하는 메소드 + private static boolean isOutOfBounds(int x, int y) { + return x < 0 || x > r - 1 || y < 0 || y > c - 1; + } + + // 해당 좌표가 공기청정기 위치인지 확인하는 메소드 + private static boolean isNearCleaner(int x, int y) { + return (x == airCleaner.front[0] && y == airCleaner.front[1]) || + (x == airCleaner.rear[0] && y == airCleaner.rear[1]); + } + + // 방에 남아있는 미세먼지의 총량을 계산하는 메소드 + private static int sum() { + int sum = 0; + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (room[i][j] != -1) sum += room[i][j]; + } + } + return sum; + } + + // 공기청정기의 위치 정보를 저장하는 클래스 + static class AirCleaner { + private int[] front; // 위쪽 공기청정기 좌표 + private int[] rear; // 아래쪽 공기청정기 좌표 + + // 공기청정기 위치를 설정하는 메소드 + public void setCleaner(int x, int y) { + if (front == null) { + front = new int[2]; + front[0] = x; + front[1] = y; + } else { + rear = new int[2]; + rear[0] = x; + rear[1] = y; + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj20207.java" "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj20207.java" new file mode 100644 index 00000000..b6e6c4e6 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:5\354\243\274\354\260\250/boj20207.java" @@ -0,0 +1,63 @@ +import java.io.*; +import java.util.*; + +public class boj20207 { + // 입출력을 위한 BufferedReader, BufferedWriter, StringBuilder 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 1년의 날짜 수 상수로 선언 + private static final int DAY = 365; + + // 각 날짜별 예약 횟수를 저장할 배열과 입력받을 예약 수 + private static int[] calendar; + private static int n; + + public static void main(String[] args) throws IOException { + init(); // 입력 및 초기화 + sb.append(solution()); // 문제 해결 및 결과 저장 + + // 결과 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 입력을 받고 배열을 초기화하는 함수 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 예약 개수 입력 + calendar = new int[DAY + 2]; // 날짜별 예약 횟수를 저장할 배열 (366일 + 여유분 1일) + + // n개의 예약 정보 처리 + for (int i = 0; i < n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int start = Integer.parseInt(st.nextToken()); // 시작일 + int end = Integer.parseInt(st.nextToken()); // 종료일 + + // 시작일부터 종료일까지 예약 횟수 증가 + for (int j = start; j <= end; j++) calendar[j]++; + } + } + + // 전체 면적(답)을 계산하는 함수 + private static int solution() { + int weight = 0; // 너비 (연속된 날짜의 수) + int height = 0; // 높이 (해당 구간의 최대 예약 횟수) + int answer = 0; // 총 면적 + + // 모든 날짜를 순회하며 면적 계산 + for (int i = 1; i <= DAY + 1; i++) { + if (calendar[i] > 0) { // 예약이 있는 날인 경우 + height = Math.max(height, calendar[i]); // 현재 구간의 최대 높이 갱신 + weight++; // 너비 증가 + } else { // 예약이 없는 날인 경우 + answer += height * weight; // 현재까지의 구간 면적 계산하여 추가 + height = 0; // 높이 초기화 + weight = 0; // 너비 초기화 + } + } + return answer; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj12761.java" "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj12761.java" new file mode 100644 index 00000000..0ed1dd74 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj12761.java" @@ -0,0 +1,84 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.StringTokenizer; + +public class boj12761 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + private static int[] dx; // 이동 가능한 방향을 저장하는 배열 + private static boolean[] visited; // 방문 여부를 체크하는 배열 + private static int n, m; // n: 시작점, m: 도착점 + private static int a, b; // a, b: 점프 가능한 거리 + + public static void main(String[] args) throws IOException{ + init(); // 초기 데이터 설정 + int answer = bfs(n); // BFS로 최단 이동 횟수 계산 + + // 결과 출력 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException{ + StringTokenizer st = new StringTokenizer(br.readLine()); + a = Integer.parseInt(st.nextToken()); // 첫 번째 점프 거리 + b = Integer.parseInt(st.nextToken()); // 두 번째 점프 거리 + n = Integer.parseInt(st.nextToken()); // 시작점 + m = Integer.parseInt(st.nextToken()); // 도착점 + + visited = new boolean[100001]; // 방문 배열 초기화 (문제의 범위가 0~100,000) + + // 이동 가능한 방향 배열 초기화: + // 한 칸 앞으로, 한 칸 뒤로, a칸 앞으로, a칸 뒤로, b칸 앞으로, b칸 뒤로, a배 앞으로, b배 앞으로 + dx = new int[] {1, -1, a, -a, b, -b, a, b}; + } + + // BFS로 최단 이동 횟수를 계산하는 메소드 + private static int bfs(int start) { + Queue queue = new ArrayDeque<>(); + visited[start] = true; + queue.add(new int[] {start, 0}); // {현재 위치, 이동 횟수} + + while (!queue.isEmpty()) { + int[] current = queue.poll(); + + // 도착점에 도달했다면 이동 횟수 반환 + if (current[0] == m) return current[1]; + + // 8가지 이동 방향에 대해 탐색 + for (int i = 0; i < 8; i++) { + int next = current[0]; + + // 앞뒤 이동 또는 점프 + if (i < 6) next += dx[i]; + // 순간이동 (곱하기) + else next *= dx[i]; + + // 범위를 벗어나거나 이미 방문한 위치면 스킵 + if (OOB(next) || visited[next]) continue; + + // 방문 처리 후 큐에 추가 + visited[next] = true; + queue.add(new int[] {next, current[1] + 1}); + } + } + + return -1; // 도달할 수 없는 경우 (이 문제에서는 발생하지 않을 것) + } + + // 범위를 벗어났는지 확인하는 메소드 + private static boolean OOB(int next) { + return next < 0 || next > 100000; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2473.java" "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2473.java" new file mode 100644 index 00000000..fe9febd9 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2473.java" @@ -0,0 +1,80 @@ +import java.io.*; +import java.util.Arrays; +import java.util.StringTokenizer; + +public class boj2473 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static long[] arr; // 입력받은 수열을 저장하는 배열 + private static long[] answer; // 결과로 선택된 세 수를 저장하는 배열 + private static int n; // 수열의 길이 + private static long min; // 세 수의 합의 최소 절대값 + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + solution(); // 문제 해결 + + // 결과 출력 + bw.write(answer[0] + " " + answer[1] + " " + answer[2] + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); + min = (long) (3e9 + 1); // 최대 가능한 값보다 큰 값으로 초기화 + + arr = new long[n]; + answer = new long[3]; + + // 수열 입력 받기 + StringTokenizer st = new StringTokenizer(br.readLine()); + for (int i = 0; i < n; i++) { + arr[i] = Long.parseLong(st.nextToken()); + } + + Arrays.sort(arr); // 이진 탐색을 위한 정렬 + } + + // 세 수의 합이 0에 가장 가까운 조합을 찾는 메소드 + private static void solution() { + // 첫 번째 수를 고정 + for (int i = 0; i < n - 2; i++) { + // 세 번째 수를 고정 (뒤에서부터) + for (int j = n - 1; j > i + 1; j--) { + int left = i + 1; // 두 번째 수의 왼쪽 범위 + int right = j - 1; // 두 번째 수의 오른쪽 범위 + + // 이진 탐색으로 두 번째 수 찾기 + while (left <= right) { + int mid = left + (right - left) / 2; + + long sum = arr[i] + arr[mid] + arr[j]; + + // 현재까지의 최소값보다 절대값이 작으면 정답 갱신 + if (min > Math.abs(sum)) { + answer[0] = arr[i]; + answer[1] = arr[mid]; + answer[2] = arr[j]; + min = Math.abs(sum); + } + + // 합이 0이면 더 이상 찾을 필요 없음 + if (sum == 0) break; + // 합이 양수면 더 작은 수를 선택해야 함 + else if (sum > 0) { + right = mid - 1; + } + // 합이 음수면 더 큰 수를 선택해야 함 + else { + left = mid + 1; + } + } + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2631.java" "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2631.java" new file mode 100644 index 00000000..552033ad --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:6\354\243\274\354\260\250/boj2631.java" @@ -0,0 +1,52 @@ +import java.io.*; + +public class boj2631 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static int[] numbers; // 입력 받은 수열을 저장하는 배열 + private static int[] dp; // 동적 프로그래밍을 위한 배열 + private static int n, max; // n: 수열의 길이, max: LIS의 최대 길이 + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + dp(); // 동적 프로그래밍으로 LIS 계산 + + // 결과 출력: 제거해야 할 최소 원소 개수 = 전체 원소 개수 - LIS 길이 + bw.write(n - max + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 수열의 길이 입력 + max = 0; // 최대 LIS 길이 초기화 + + numbers = new int[n]; // 수열 배열 초기화 + dp = new int[n]; // DP 배열 초기화 + + // 수열 입력 받기 + for (int i = 0; i < n; i++) { + numbers[i] = Integer.parseInt(br.readLine()); + dp[i] = 1; // 모든 위치에서 LIS의 최소 길이는 1 + } + } + + // 동적 프로그래밍으로 LIS(최장 증가 부분 수열)를 계산하는 메소드 + private static void dp() { + for (int i = 1; i < n; i++) { + for (int j = i - 1; j > -1; j--) { + // 현재 위치(i)의 값이 이전 위치(j)의 값보다 크면 + if (numbers[i] > numbers[j]) { + // i 위치까지의 LIS 길이 업데이트 + dp[i] = Math.max(dp[i], dp[j] + 1); + // 전체 최대 LIS 길이 업데이트 + max = Math.max(max, dp[i]); + } + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj10711.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj10711.java" new file mode 100644 index 00000000..2e98c9d2 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj10711.java" @@ -0,0 +1,145 @@ +import java.io.*; +import java.util.*; +import java.awt.Point; + +/** + * 백준 10711번 - 모래성 + * + * 문제 개요: + * - 모래성은 1~9 사이의 튼튼함을 가짐 (숫자가 클수록 튼튼함) + * - 파도가 칠 때마다 주변 8방향의 빈 칸 수가 모래성의 튼튼함보다 크거나 같으면 무너짐 + * - 모든 모래성이 무너지지 않을 때까지 몇 번의 파도가 필요한지 계산 + */ +public class boj10711 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 8방향 이동을 위한 방향 배열 (상, 우상, 우, 우하, 하, 좌하, 좌, 좌상) + private static final int[] dx = {1, 1, 1, 0, -1, -1, -1, 0}; + private static final int[] dy = {1, -1, 0, 1, 1, -1, 0, -1}; + + private static List castles; // 모든 모래성의 위치 저장 + private static Queue toRemove; // 다음 파도에 무너질 모래성들을 저장하는 큐 + private static boolean[][] visited; // 이미 무너질 것으로 판단된 모래성 표시 + private static int[][] map; // 모래성 맵 (-1: 빈 칸, 1~9: 모래성 튼튼함) + private static int h, w; // 맵의 높이와 너비 + + /** + * 메인 메소드 + * 초기화 후 모든 모래성이 무너질 때까지 파도를 시뮬레이션 + */ + public static void main(String[] args) throws IOException { + // 입력 처리 및 초기 상태 설정 + init(); + + // 파도 시뮬레이션 + int time = 0; + while (!toRemove.isEmpty()) { + delete(); // 파도에 의해 모래성 무너뜨리기 + time++; // 파도 횟수 증가 + } + + // 결과 출력 + bw.write(time + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 초기화 메소드 + * 입력을 받아 맵을 생성하고 첫 파도에 무너질 모래성 식별 + */ + private static void init() throws IOException { + // 맵 크기 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + h = Integer.parseInt(st.nextToken()); + w = Integer.parseInt(st.nextToken()); + + // 데이터 구조 초기화 + map = new int[h][w]; + visited = new boolean[h][w]; + castles = new ArrayList<>(); + + // 맵 입력 처리 + for (int i = 0; i < h; i++) { + char[] input = br.readLine().toCharArray(); + for (int j = 0; j < w; j++) { + if (input[j] == '.') { + map[i][j] = -1; // 빈 칸은 -1로 표시 + } else { + map[i][j] = input[j] - '0'; // 모래성은 튼튼함 값 저장 + castles.add(new Point(i, j)); // 모래성 위치 저장 + } + } + } + + // 첫 파도에 무너질 모래성 식별 + toRemove = new ArrayDeque<>(); + for (Point castle : castles) { + int wave = 0; // 주변 빈 칸 수 + for (int i = 0; i < 8; i++) { + int nx = castle.x + dx[i]; + int ny = castle.y + dy[i]; + + // 범위를 벗어나지 않고 빈 칸인 경우 파도 카운트 증가 + if (!OOB(nx, ny) && map[nx][ny] == -1) wave++; + } + + // 파도 수가 모래성의 튼튼함 이상이면 무너질 예정 + if (wave >= map[castle.x][castle.y]) { + toRemove.add(castle); + visited[castle.x][castle.y] = true; // 이미 처리 표시 + } + } + } + + /** + * 파도가 치는 효과를 시뮬레이션하는 메소드 + * 현재 큐에 있는 모래성을 모두 무너뜨리고, 다음 파도에 무너질 모래성을 큐에 추가 + */ + private static void delete() { + int size = toRemove.size(); // 현재 파도에 무너질 모래성 수 + + // 현재 파도에 무너질 모든 모래성 처리 + for (int i = 0; i < size; i++) { + Point current = toRemove.poll(); + map[current.x][current.y] = -1; // 모래성을 빈 칸으로 변경 + + // 무너진 모래성 주변 8방향 확인 + for (int d = 0; d < 8; d++) { + int nx = current.x + dx[d]; + int ny = current.y + dy[d]; + + // 유효하지 않은 위치, 이미 빈 칸이거나 처리 예정인 모래성은 건너뜀 + if (OOB(nx, ny) || map[nx][ny] == -1 || visited[nx][ny]) continue; + + int wave = 0; // 주변 빈 칸 수 계산 + for (int j = 0; j < 8; j++) { + int nnx = nx + dx[j]; + int nny = ny + dy[j]; + + if (OOB(nnx, nny)) continue; + if (map[nnx][nny] == -1) wave++; + } + + // 파도 수가 모래성의 튼튼함 이상이면 다음 파도에 무너질 예정 + if (wave >= map[nx][ny]) { + visited[nx][ny] = true; // 중복 처리 방지 + toRemove.add(new Point(nx, ny)); + } + } + } + } + + /** + * 좌표가 맵 범위를 벗어나는지 확인하는 메소드 + * @param nx x 좌표 + * @param ny y 좌표 + * @return 범위를 벗어나면 true, 그렇지 않으면 false + */ + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > h - 1 || ny < 0 || ny > w - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj12026.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj12026.java" new file mode 100644 index 00000000..2d598129 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj12026.java" @@ -0,0 +1,86 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Arrays; + +/** + * 메인 클래스 - JOB 문자 점프 문제 해결 + * 규칙: J -> O -> B -> J 순서로만 이동 가능, 이동 비용은 거리의 제곱 + */ +public class boj12026 { + // 입출력을 위한 버퍼 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 도달할 수 없는 경우를 표현하는 최대값 + private static final int MAX = 1000000000; + + private static char[] road; // 문자열(길)을 저장할 배열 + private static int[] dp; // 각 위치까지의 최소 비용을 저장할 DP 배열 + private static int n, answer; // 문자열 길이와 정답을 저장할 변수 + + /** + * 메인 메서드 + */ + public static void main(String[] args) throws IOException { + init(); // 입력 및 초기화 + DP(); // 동적 계획법 실행 + + // 마지막 위치의 최소 비용을 정답으로 설정 + answer = dp[n - 1]; + // 불가능한 경우(MAX 값이 그대로인 경우) -1 출력 + if (answer == MAX) answer = -1; + + // 결과 출력 및 자원 정리 + bw.write(answer + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 초기 설정을 하는 메서드 + */ + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 문자열 길이 입력 + answer = 0; // 정답 초기화 + + road = br.readLine().toCharArray(); // 문자열을 문자 배열로 변환 + dp = new int[n]; // DP 배열 초기화 + + // 모든 위치를 일단 도달 불가능한 상태(MAX)로 설정 + Arrays.fill(dp, MAX); + // 시작 위치(0번 인덱스)의 비용은 0 + dp[0] = 0; + } + + /** + * 동적 계획법을 이용해 각 위치까지의 최소 비용을 계산하는 메서드 + * J->O->B->J 순서로만 이동 가능하며, 이동 비용은 거리의 제곱 + */ + private static void DP() { + for (int i = 1; i < n; i++) { // 모든 위치에 대해 + for (int j = 0; j < i; j++) { // 이전 모든 위치에서 현재 위치로 오는 경우 검사 + char prev = getNextChar(road[i]); // i 위치 이전에 올 수 있는 문자 + + // i 위치의 문자가 j 다음에 올 수 있는 문자이고, j 위치에 도달 가능한 경우 + if (road[j] == prev && dp[j] != MAX) { + int k = i - j; // 두 위치 간 거리 + dp[i] = Math.min(dp[i], dp[j] + k * k); // 최소 비용 갱신 + } + } + } + } + + /** + * 현재 문자 다음에 와야 하는 문자를 반환하는 메서드 + * J -> O -> B -> J 순서를 정의 + */ + private static char getNextChar(char c) { + if (c == 'J') return 'O'; // J 다음에는 O + if (c == 'O') return 'B'; // O 다음에는 B + if (c == 'B') return 'J'; // B 다음에는 J + return '0'; // 그 외의 경우 (예외 처리) + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16237.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16237.java" new file mode 100644 index 00000000..164b535b --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16237.java" @@ -0,0 +1,246 @@ +import java.io.*; +import java.util.*; + +public class boj16237 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 상어의 이동 방향 (0: 정지, 1: 위, 2: 아래, 3: 왼쪽, 4: 오른쪽) + private static final int[] dx = {0, -1, 1, 0, 0}; + private static final int[] dy = {0, 0, 0, -1, 1}; + private static Cell[][] map; // 격자 맵 + private static List sharks; // 상어 목록 + private static int n, m, k, answer; // n: 맵 크기, m: 상어 수, k: 냄새 지속 시간, answer: 정답 + + public static void main(String[] args) throws IOException { + init(); // 초기 설정 + + // 최대 1000초까지 시뮬레이션 + for (int i = 0; i <= 1000; i++) { + if (sharks.size() == 1) { // 1번 상어만 남으면 종료 + answer = i; + break; + } + chooseDir(); // 각 상어의 이동 방향 결정 + move(); // 상어 이동 + battle(); // 같은 칸에 있는 상어들 중 가장 작은 번호만 남김 + downSmell(); // 냄새 시간 감소 + } + + bw.write(answer + "\n"); // 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 설정을 처리하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + k = Integer.parseInt(st.nextToken()); + answer = -1; // 1000초 이내에 1번 상어만 남지 않으면 -1 출력 + + map = new Cell[n][n]; + sharks = new ArrayList<>(); + + // 맵 정보 입력 + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < n; j++) { + map[i][j] = new Cell(); + + int num = Integer.parseInt(st.nextToken()); + if (num > 0) { // 상어가 있는 위치 + Shark shark = new Shark(i, j, num); + map[i][j].sharkNum.add(num); + sharks.add(shark); + + map[i][j].smell = new Smell(num, k); // 냄새 설정 + } + } + } + + // 상어를 번호 순으로 정렬 + sharks.sort((o1, o2) -> { + return o1.num - o2.num; + }); + + // 상어의 초기 방향 입력 + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < m; i++) { + sharks.get(i).dir = Integer.parseInt(st.nextToken()); + } + + // 각 상어의 방향 우선순위 입력 + for (int i = 0; i < m; i++) { + for (int j = 1; j <= 4; j++) { // 1: 위, 2: 아래, 3: 왼쪽, 4: 오른쪽 + st = new StringTokenizer(br.readLine()); + for (int k = 0; k < 4; k++) { + sharks.get(i).priorityDir[j][k] = Integer.parseInt(st.nextToken()); + } + } + } + } + + // 모든 상어의 이동 방향을 결정하는 메소드 + private static void chooseDir() { + for (Shark shark : sharks) { + shark.dir = chooseDir(shark); + } + } + + // 개별 상어의 이동 방향을 결정하는 메소드 + private static int chooseDir(Shark shark) { + int[] priorityDir = shark.priorityDir[shark.dir]; // 현재 방향에 따른 우선순위 + + // 1. 냄새가 없는 칸 찾기 + for (int i = 0; i < 4; i++) { + int nx = shark.x + dx[priorityDir[i]]; + int ny = shark.y + dy[priorityDir[i]]; + + if (!OOB(nx, ny) && map[nx][ny].smell.sharkNum == 0) { + return priorityDir[i]; + } + } + + // 2. 자신의 냄새가 있는 칸 찾기 + for (int i = 0; i < 4; i++) { + int nx = shark.x + dx[priorityDir[i]]; + int ny = shark.y + dy[priorityDir[i]]; + + if (!OOB(nx, ny) && map[nx][ny].smell.sharkNum == shark.num) { + return priorityDir[i]; + } + } + + return 0; // 여기까지 오면 안됨 + } + + // 맵 범위를 벗어났는지 확인하는 메소드 + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1; + } + + // 상어 이동을 처리하는 메소드 + private static void move() { + Cell[][] board = new Cell[n][n]; // 이동 후의 새로운 맵 + + // 냄새 정보만 복사 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = new Cell(); + if (map[i][j].smell.sharkNum == 0) continue; + board[i][j].smell = map[i][j].smell; + } + } + + // 각 상어 이동 + for (Shark shark : sharks) { + int nx = shark.x + dx[shark.dir]; + int ny = shark.y + dy[shark.dir]; + + shark.x += dx[shark.dir]; // 상어 위치 업데이트 + shark.y += dy[shark.dir]; + + board[nx][ny].smell = new Smell(shark.num, k); // 새 위치에 냄새 남김 + board[nx][ny].sharkNum.add(shark.num); // 새 위치에 상어 정보 추가 + } + + map = board; // 맵 업데이트 + } + + // 같은 칸에 여러 상어가 있을 때 번호가 가장 작은 상어만 남기는 메소드 + private static void battle() { + boolean[][] visited = new boolean[n][n]; + List toRemove = new ArrayList<>(); // 제거할 상어 번호 + + for (Shark shark : sharks) { + int x = shark.x; + int y = shark.y; + + if (visited[x][y]) continue; // 이미 처리한 칸은 스킵 + visited[x][y] = true; + + if (map[x][y].sharkNum.size() == 1) continue; // 상어가 하나만 있으면 스킵 + Collections.sort(map[x][y].sharkNum); // 번호 순 정렬 + + // 가장 작은 번호를 제외한 모든 상어 제거 + for (int i = map[x][y].sharkNum.size() - 1; i > 0; i--) { + int sharkNumToRemove = map[x][y].sharkNum.get(i); + toRemove.add(sharkNumToRemove); + map[x][y].sharkNum.remove(i); + } + map[x][y].smell.sharkNum = map[x][y].sharkNum.get(0); // 냄새 업데이트 + } + + // 상어 목록에서 제거할 상어들 제거 + for (int i = sharks.size() - 1; i >= 0; i--) { + if (toRemove.contains(sharks.get(i).num)) { + sharks.remove(i); + } + } + } + + // 냄새 시간을 감소시키는 메소드 + private static void downSmell() { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + // 냄새가 없거나 현재 상어가 있는 칸은 스킵 + if (map[i][j].smell.sharkNum == 0 || !map[i][j].sharkNum.isEmpty()) continue; + + // 냄새 시간 감소 + if (map[i][j].smell.time > 0) { + map[i][j].smell.time--; + } + + // 냄새 시간이 0이 되면 냄새 제거 + if (map[i][j].smell.time == 0) { + map[i][j].smell.sharkNum = 0; + } + } + } + } + + // 맵의 각 칸을 표현하는 클래스 + static class Cell { + Smell smell; // 냄새 정보 + List sharkNum; // 해당 칸에 있는 상어 번호 목록 + + Cell () { + smell = new Smell(); + sharkNum = new ArrayList<>(); + } + } + + // 냄새 정보를 표현하는 클래스 + static class Smell { + int sharkNum; // 냄새를 남긴 상어 번호 + int time; // 냄새가 남아있는 시간 + + Smell(int sharkNum, int time) { + this.sharkNum = sharkNum; + this.time = time; + } + + Smell () { + this.sharkNum = 0; + this.time = 0; + } + } + + // 상어 정보를 표현하는 클래스 + static class Shark { + int x, y; // 상어의 위치 + int num; // 상어 번호 + int dir; // 현재 방향 + int[][] priorityDir; // 방향별 우선순위 + + Shark(int x, int y, int num) { + this.x = x; + this.y = y; + this.num = num; + this.dir = 0; + priorityDir = new int[5][4]; // [현재방향][우선순위] + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16564.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16564.java" new file mode 100644 index 00000000..4b781a53 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16564.java" @@ -0,0 +1,91 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.StringTokenizer; + +public class boj16564 { + // 입력을 효율적으로 읽기 위한 BufferedReader + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + + // 출력을 효율적으로 쓰기 위한 BufferedWriter + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 캐릭터들의 레벨을 저장할 배열 + private static int[] levels; + + // n: 캐릭터의 수, k: 사용 가능한 포인트, min: 가장 낮은 레벨 + private static int n, k, min; + + public static void main(String[] args) throws IOException { + // 입력 데이터와 변수 초기화 + init(); + + // 이진 탐색을 통해 달성 가능한 최대 레벨 찾기 + bw.write(binarySearch() + "\n"); + + // 출력 버퍼 비우기 및 리소스 닫기 + bw.flush(); + bw.close(); + br.close(); + } + + // 입력 데이터 초기화 메서드 + private static void init() throws IOException { + // 첫 줄에서 n(캐릭터 수)과 k(사용 가능한 포인트) 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + k = Integer.parseInt(st.nextToken()); + + // 레벨 배열 생성 및 최소 레벨 찾기 + levels = new int[n]; + min = (int)1e9; + + // 각 캐릭터의 현재 레벨 입력 및 최소 레벨 갱신 + for (int i = 0; i < n; i++) { + levels[i] = Integer.parseInt(br.readLine()); + min = Math.min(min, levels[i]); + } + } + + // 이진 탐색을 통해 최대 달성 가능한 레벨 찾는 메서드 + private static int binarySearch() { + // 탐색 범위 설정: 최소 레벨부터 (최소 레벨 + 사용 가능한 포인트)까지 + int left = min; + int right = min + k; + + while (left <= right) { + // 중간값 계산 (오버플로우 방지를 위한 최적화된 방식) + int mid = left + (right - left) / 2; + + // 현재 중간값 레벨로 모든 캐릭터를 업그레이드할 수 있는지 확인 + if (isPossible(mid)) { + // 가능하다면 더 높은 레벨 탐색 + left = mid + 1; + } + else { + // 불가능하다면 더 낮은 레벨 탐색 + right = mid - 1; + } + } + + // 최대로 달성 가능한 레벨 반환 + return right; + } + + // 특정 목표 레벨로 모든 캐릭터를 업그레이드할 수 있는지 확인하는 메서드 + private static boolean isPossible(int target) { + // 목표 레벨로 업그레이드하는 데 필요한 총 포인트 계산 + long needPoints = 0; + for (int level : levels) { + // 현재 레벨이 목표 레벨보다 낮은 경우 필요한 포인트 추가 + if (level < target) { + needPoints += (target - level); + } + } + + // 필요한 포인트가 사용 가능한 포인트(k)보다 작거나 같은지 확인 + return needPoints <= k; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16918.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16918.java" new file mode 100644 index 00000000..eba04c9a --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16918.java" @@ -0,0 +1,135 @@ +import java.io.*; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * 봄버맨 게임 시뮬레이션 클래스 + * 매 초마다 봄버맨의 행동을 시뮬레이션하고 결과를 출력합니다. + */ +public class boj16918 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final char EMPTY = '.'; // 빈 칸을 나타내는 문자 + private static final char BOMB = 'O'; // 폭탄을 나타내는 문자 + private static final List POINTS = new ArrayList<>(); // 폭탄의 위치를 저장하는 리스트 + private static final StringBuilder sb = new StringBuilder(); // 결과 문자열 생성을 위한 StringBuilder + private static final int[] dx = {1, 0, -1, 0}; // 상하좌우 이동을 위한 x축 변화량 + private static final int[] dy = {0, 1, 0, -1}; // 상하좌우 이동을 위한 y축 변화량 + private static char[][] zone; // 격자판(게임 맵) + private static int r, c, n; // r: 행 수, c: 열 수, n: 시간(초) + + /** + * 메인 메소드 + * 초기화 후 n초 동안의 봄버맨 게임을 시뮬레이션합니다. + */ + public static void main(String[] args) throws IOException{ + // 입력을 받아 초기 상태 설정 + init(); + + // n초 동안의 게임 시뮬레이션 + // 1초: 초기 상태 그대로 유지 + // 짝수 초: 모든 빈 칸에 폭탄 설치 + // 홀수 초(3초 이상): 폭탄 폭발 + for (int time = 2; time <= n; time++) { + if (time % 2 == 0) fillBomb(); // 짝수 초에는 빈 칸에 폭탄 설치 + else boom(); // 홀수 초에는 폭탄 폭발 + } + + // 최종 상태 출력 + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + sb.append(zone[i][j]); + } + sb.append("\n"); + } + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 초기 상태를 설정하는 메소드 + * 입력을 받아 격자판과 초기 폭탄 위치를 설정합니다. + */ + private static void init() throws IOException{ + // 행, 열, 시간 입력 받기 + StringTokenizer st = new StringTokenizer(br.readLine()); + r = Integer.parseInt(st.nextToken()); + c = Integer.parseInt(st.nextToken()); + n = Integer.parseInt(st.nextToken()); + + // 격자판 초기화 + zone = new char[r][c]; + + // 격자판 상태 입력 받기 + for (int i = 0; i < r; i++) { + char[] input = br.readLine().toCharArray(); + for (int j = 0; j < c; j++) { + zone[i][j] = input[j]; + // 폭탄 위치 저장 + if (zone[i][j] == BOMB) POINTS.add(new Point(i, j)); + } + } + } + + /** + * 빈 칸에 폭탄을 설치하는 메소드 + * 격자판의 모든 빈 칸에 폭탄을 설치합니다. + */ + private static void fillBomb() { + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (zone[i][j] == EMPTY) zone[i][j] = BOMB; + } + } + } + + /** + * 폭탄을 폭발시키는 메소드 + * 현재 존재하는 모든 폭탄을 폭발시키고, 폭발 범위(상하좌우)도 함께 제거합니다. + */ + private static void boom() { + boolean[][] visited = new boolean[r][c]; // 이미 처리한 칸 표시 + + // 저장된 모든 폭탄 위치에 대해 폭발 처리 + for (Point point : POINTS) { + // 폭탄 위치를 빈 칸으로 변경 + zone[point.x][point.y] = EMPTY; + visited[point.x][point.y] = true; + + // 폭탄의 상하좌우 처리 + for (int i = 0; i < 4; i++) { + int nx = point.x + dx[i]; + int ny = point.y + dy[i]; + + // 격자판 범위를 벗어나거나 이미 처리한 칸이면 건너뜀 + if (OOB(nx, ny) || visited[nx][ny]) continue; + + // 해당 칸을 처리했음을 표시하고 빈 칸으로 변경 + visited[nx][ny] = true; + zone[nx][ny] = EMPTY; + } + } + + // 폭발 후 남아있는 폭탄 위치 갱신 + POINTS.clear(); + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (zone[i][j] == BOMB) POINTS.add(new Point(i, j)); + } + } + } + + /** + * 좌표가 격자판 범위를 벗어나는지 확인하는 메소드 + * @param nx x 좌표 + * @param ny y 좌표 + * @return 범위를 벗어나면 true, 그렇지 않으면 false + */ + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > r - 1 || ny < 0 || ny > c - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16947.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16947.java" new file mode 100644 index 00000000..208c2d38 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16947.java" @@ -0,0 +1,157 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeSet; + +public class boj16947 { + // 입출력을 위한 객체 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 그래프와 결과 저장을 위한 변수 선언 + private static List> graph; // 그래프의 인접 리스트 표현 + private static Set cycleNodes; // 사이클에 포함된 노드들을 저장하는 집합 + private static int[] distance; // 각 노드에서 사이클까지의 최단 거리 + private static boolean[] visited; // DFS 및 BFS에서 노드 방문 여부 확인 + private static int n; // 노드의 개수 + + public static void main(String[] args) throws IOException{ + // 그래프 초기화 및 입력 처리 + init(); + + // 1번 노드부터 DFS 탐색 시작하여 사이클 찾기 + List temp = new ArrayList<>(); // 현재 DFS 경로를 저장하는 임시 리스트 + DFS(1, -1, temp); // 시작 노드 1, 부모 노드 없음(-1) + + // BFS로 사이클에서 각 노드까지의 최단 거리 계산 + BFS(); + + // 결과 출력 + for (int i = 1; i <= n; i++) { + sb.append(distance[i]).append(" "); + } + sb.setLength(sb.length() - 1); // 마지막 공백 제거 + + bw.write(sb.toString() + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받아 그래프와 필요한 데이터 구조 초기화 + */ + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 노드 개수 입력 + + // 데이터 구조 초기화 + graph = new ArrayList<>(); + cycleNodes = new TreeSet<>(); // 사이클 노드 정렬을 위해 TreeSet 사용 + distance = new int[n + 1]; + visited = new boolean[n + 1]; + + // 그래프의 인접 리스트 초기화 (1-indexed) + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + + // 간선 정보 입력 + for (int i = 0; i < n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int node1 = Integer.parseInt(st.nextToken()); + int node2 = Integer.parseInt(st.nextToken()); + + // 양방향 그래프이므로 양쪽에 추가 + graph.get(node1).add(node2); + graph.get(node2).add(node1); + } + } + + /** + * DFS를 사용하여 그래프에서 사이클 찾기 + * @param node 현재 방문 중인 노드 + * @param parent 현재 노드의 부모 노드 (직전에 방문한 노드) + * @param temp 현재까지의 DFS 경로를 저장하는 리스트 + */ + private static void DFS(int node, int parent, List temp) { + visited[node] = true; // 현재 노드 방문 표시 + temp.add(node); // 현재 경로에 노드 추가 + + // 현재 노드의 모든 인접 노드 확인 + for (int next : graph.get(node)) { + // 부모 노드는 건너뛰기 (양방향 그래프에서 바로 되돌아가는 것 방지) + if (next == parent) continue; + + if (visited[next]) { + // 이미 방문한 노드를 다시 만났다면 사이클 가능성 있음 + int index = temp.indexOf(next); + if (index != -1) { // 현재 경로에 있는 노드라면 사이클 발견 + // 사이클에 포함된 노드들 추가 (index부터 현재까지) + for (int i = index; i < temp.size(); i++) { + cycleNodes.add(temp.get(i)); + } + return; // 사이클 찾았으므로 탐색 중단 + } + } else { + // 아직 방문하지 않은 노드면 계속 탐색 + DFS(next, node, temp); + // 사이클을 찾았으면 더 이상 탐색하지 않고 반환 + if (!cycleNodes.isEmpty()) return; + } + } + + // 백트래킹: 더 이상 탐색할 곳이 없으면 현재 노드를 경로에서 제거 + temp.remove(temp.size() - 1); + } + + /** + * BFS를 사용하여 각 노드에서 사이클까지의 최단 거리 계산 + */ + private static void BFS() { + Queue q = new ArrayDeque<>(); + // visited 배열 초기화 (DFS에서 사용한 후이므로) + visited = new boolean[n + 1]; + + // 사이클에 속한 모든 노드를 시작점으로 큐에 추가 + for (int node : cycleNodes) { + visited[node] = true; + distance[node] = 0; // 사이클 노드의 거리는 0 + q.add(new int[] {node, 0}); // [노드 번호, 거리] + } + + // BFS 탐색 시작 + while (!q.isEmpty()) { + int[] current = q.poll(); + int currentNode = current[0]; + int currentDist = current[1]; + + // 현재 노드의 모든 인접 노드 확인 + for (int next : graph.get(currentNode)) { + if (visited[next]) continue; // 이미 방문한 노드는 건너뛰기 + + visited[next] = true; + + // 인접 노드가 사이클에 포함되어 있으면 거리는 그대로 + // (이 부분은 실제로는 실행되지 않을 것임 - 이미 사이클 노드는 visited=true로 표시되어 있음) + if (cycleNodes.contains(next)) { + distance[next] = currentDist; + q.add(new int[] {next, currentDist}); + } + else { + // 사이클에 포함되지 않은 노드는 거리 1 증가 + distance[next] = currentDist + 1; + q.add(new int[] {next, currentDist + 1}); + } + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16974.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16974.java" new file mode 100644 index 00000000..ea36fd2e --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj16974.java" @@ -0,0 +1,92 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.StringTokenizer; + +/** + * 햄버거 먹는 문제 - 재귀적 구조의 햄버거에서 특정 위치까지 먹었을 때 패티의 개수를 계산 + */ +public class boj16974 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static long[] burger; // 각 레벨별 햄버거의 총 길이를 저장하는 배열 + private static long[] patties; // 각 레벨별 햄버거에 포함된 패티의 총 개수를 저장하는 배열 + private static int n; // 햄버거의 레벨 + private static long x; // 먹을 햄버거의 길이 + + public static void main(String[] args) throws IOException { + // 입력을 받고 초기화 + init(); + + // 패티 개수 계산 및 출력 + bw.write(countPatties(n, x) + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 햄버거와 패티 배열을 초기화하는 메소드 + */ + private static void init() throws IOException { + // 입력 받기 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 햄버거 레벨 + x = Long.parseLong(st.nextToken()); // 먹을 햄버거 길이 + + // 배열 초기화 + burger = new long[n + 1]; // 각 레벨별 햄버거 길이 + patties = new long[n + 1]; // 각 레벨별 패티 개수 + + // 레벨 0 햄버거는 패티 하나 (P) + burger[0] = 1; // 패티 하나의 길이 + patties[0] = 1; // 패티 개수 + + // 각 레벨별 햄버거 길이와 패티 수 계산 + // 레벨 L 햄버거 = B + 레벨 L-1 햄버거 + P + 레벨 L-1 햄버거 + B + // B는 빵(길이 1), P는 패티(길이 1) + for (int i = 1; i <= n; i++) { + burger[i] = 2 * burger[i - 1] + 3; // 총 길이: 이전 레벨 햄버거 2개 + 빵 2개 + 패티 1개 + patties[i] = 2 * patties[i - 1] + 1; // 패티 수: 이전 레벨 패티 수 2배 + 중간 패티 1개 + } + } + + /** + * 레벨 level의 햄버거에서 x만큼 먹었을 때 먹은 패티의 개수를 계산하는 메소드 + * + * @param level 햄버거의 레벨 + * @param x 먹은 햄버거의 길이 + * @return 먹은 패티의 개수 + */ + private static long countPatties(int level, long x) { + // 기저 사례: 레벨 0 햄버거는 패티 하나 + if (level == 0) return patties[0]; + + // 첫 번째 빵만 먹었을 경우 + if (x == 1) return 0; + + // 햄버거 구조: B + [레벨 L-1 햄버거] + P + [레벨 L-1 햄버거] + B + + // 케이스 1: 첫 번째 빵과 첫 번째 레벨 L-1 햄버거의 일부를 먹은 경우 + if (x <= 1 + burger[level - 1]) { + return countPatties(level - 1, x - 1); // 빵 이후부터의 패티 개수 계산 + } + + // 케이스 2: 첫 번째 빵, 첫 번째 레벨 L-1 햄버거, 중간 패티까지 먹은 경우 + if (x == 1 + burger[level - 1] + 1) { + return patties[level - 1] + 1; // 첫 번째 레벨 L-1 햄버거의 패티 + 중간 패티 + } + + // 케이스 3: 첫 번째 빵, 첫 번째 레벨 L-1 햄버거, 중간 패티, 두 번째 레벨 L-1 햄버거의 일부를 먹은 경우 + if (x <= 1 + burger[level - 1] + 1 + burger[level - 1]) { + // 첫 번째 레벨 L-1 햄버거의 패티 + 중간 패티 + 두 번째 레벨 L-1 햄버거에서 먹은 패티 + return patties[level - 1] + 1 + countPatties(level - 1, x - (1 + burger[level - 1] + 1)); + } + + // 케이스 4: 햄버거 전체를 먹은 경우 + return patties[level]; // 전체 패티 개수 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj17265.java" "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj17265.java" new file mode 100644 index 00000000..ec45bfdc --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:7\354\243\274\354\260\250/boj17265.java" @@ -0,0 +1,123 @@ +import java.io.*; +import java.util.*; + +/** + * 백준 17265번 - 나의 인생에는 수학과 함께 + * + * 문제 개요: + * - N×N 크기의 격자에 숫자(0-5)와 연산자(+,-,*)가 교대로 채워져 있음 + * - 좌상단(0,0)에서 우하단(N-1,N-1)까지 이동 + * - 이동 방향은 오른쪽과 아래쪽만 가능 + * - 경로에서 만난 숫자와 연산자를 순서대로 계산 + * - 가능한 모든 경로 중에서 최댓값과 최솟값을 구하는 문제 + */ +public class boj17265 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + // 이동 방향 (오른쪽, 아래쪽) + private static final int[] dx = {1, 0}; + private static final int[] dy = {0, 1}; + + private static char[][] map; // 격자 맵 (숫자와 연산자) + private static int n; // 격자 크기 + private static int max, min; // 최댓값, 최솟값 + + /** + * 메인 메소드 + * 입력 처리 후 DFS로 모든 경로 탐색 + */ + public static void main(String[] args) throws IOException { + // 입력 처리 및 초기화 + init(); + + // DFS로 모든 가능한 경로 탐색 + // 시작점(0,0)의 값을 초기 결과로 설정, 연산자는 아직 없으므로 'a'로 임시 설정 + DFS(0, 0, map[0][0] - '0', 'a'); + + // 결과 출력 + bw.write(max + " " + min + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 처리하고 변수들을 초기화하는 메소드 + */ + private static void init() throws IOException { + // 격자 크기 입력 + n = Integer.parseInt(br.readLine()); + + // 최댓값과 최솟값 초기화 (가능한 범위를 고려하여 설정) + // 최대 5^5 = 3125, 최소 -3125 + max = -3125; + min = 3125; + + // 격자 맵 초기화 및 입력 + map = new char[n][n]; + for (int i = 0; i < n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + for (int j = 0; j < n; j++) { + map[i][j] = st.nextToken().charAt(0); + } + } + } + + /** + * DFS로 모든 가능한 경로를 탐색하는 메소드 + * @param x 현재 x좌표 + * @param y 현재 y좌표 + * @param result 현재까지의 계산 결과 + * @param op 마지막으로 만난 연산자 (초기값 'a'는 연산자가 없음을 의미) + */ + private static void DFS(int x, int y, int result, char op) { + // 목적지에 도달했을 때 최댓값, 최솟값 갱신 + if (x == n - 1 && y == n - 1) { + max = Math.max(max, result); + min = Math.min(min, result); + return; + } + + // 가능한 두 방향(오른쪽, 아래쪽)으로 탐색 + for (int i = 0; i < 2; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + + // 격자 범위를 벗어나면 건너뜀 + if (OOB(nx, ny)) continue; + + // 숫자인 경우 (0-5) + if (map[nx][ny] >= '0' && map[nx][ny] <= '5') { + int num = map[nx][ny] - '0'; + + // 이전 연산자에 따라 계산 후 다음 DFS 호출 + // 연산자가 없는 경우('a')는 첫 번째 숫자이므로 계산하지 않음 + if (op == '+') { + DFS(nx, ny, result + num, 'a'); // 덧셈 수행 + } else if (op == '-') { + DFS(nx, ny, result - num, 'a'); // 뺄셈 수행 + } else if (op == '*') { + DFS(nx, ny, result * num, 'a'); // 곱셈 수행 + } else { + // 첫 번째 숫자인 경우 (op가 'a'인 경우) + DFS(nx, ny, num, 'a'); + } + } + // 연산자인 경우 (+, -, *) + else { + // 현재 연산자를 저장하고 결과는 변경하지 않음 + DFS(nx, ny, result, map[nx][ny]); + } + } + } + + /** + * 좌표가 격자 범위를 벗어나는지 확인하는 메소드 + * @param nx x 좌표 + * @param ny y 좌표 + * @return 범위를 벗어나면 true, 그렇지 않으면 false + */ + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj10836.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj10836.java" new file mode 100644 index 00000000..9355a85d --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj10836.java" @@ -0,0 +1,106 @@ +import java.io.*; +import java.util.*; +import java.awt.*; +public class Main { + // 입출력을 위한 버퍼 리더와 라이터 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 맵과 성장 정보를 저장할 2차원 배열 + private static int[][] map; // 각 위치의 높이를 저장하는 배열 + private static int[][] grow; // 성장 정보를 저장하는 배열 + + // 맵 크기(m)와 날짜 수(n) + private static int n, m; + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // n일 동안 첫 번째 성장 처리 + for (int day = 0; day < n; day++) { + firstGrow(day); + } + + // 다른 위치의 성장 처리 + otherGrow(); + + // 결과 맵을 문자열로 변환하여 출력 준비 + for (int i = 0; i < m; i++) { + for (int j = 0; j < m; j++) { + sb.append(map[i][j]).append(" "); + } + // 마지막 공백 제거하고 줄바꿈 추가 + sb.setLength(sb.length() - 1); + sb.append("\n"); + } + + // 최종 결과 출력 및 리소스 해제 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기화 함수: 입력을 받아 맵과 성장 정보 초기화 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + m = Integer.parseInt(st.nextToken()); // 맵 크기 + n = Integer.parseInt(st.nextToken()); // 날짜 수 + + // 맵과 성장 정보 배열 초기화 + map = new int[m][m]; // m x m 크기의 맵 + grow = new int[n][3]; // n일 동안의 성장 정보(각 일자별로 3가지 성장 유형) + + // 맵을 1로 초기화 (모든 위치의 초기 높이는 1) + for (int i = 0; i < m; i++) { + Arrays.fill(map[i], 1); + } + + // n일 동안의 성장 정보 입력 받기 + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + grow[i][0] = Integer.parseInt(st.nextToken()); // 첫 번째 성장 유형의 수 + grow[i][1] = Integer.parseInt(st.nextToken()); // 두 번째 성장 유형의 수 + grow[i][2] = Integer.parseInt(st.nextToken()); // 세 번째 성장 유형의 수 + } + } + + // 첫 번째 성장 처리 함수 (특정 일자의 성장 정보 적용) + private static void firstGrow(int day) { + int index = 0; // 성장 유형 인덱스 + + // 왼쪽 세로 경계선(아래에서 위로) 성장 처리 + for (int i = m - 1; i > 0; i--) { + // 해당 성장 유형의 남은 수가 0이면 다음 유형으로 이동 + while (grow[day][index] == 0) index++; + // 현재 위치에 성장 유형 인덱스만큼 높이 증가 + map[i][0] += index; + // 해당 성장 유형 사용 횟수 감소 + grow[day][index]--; + } + + // 위쪽 가로 경계선(왼쪽에서 오른쪽으로) 성장 처리 + for (int i = 0; i < m; i++) { + // 해당 성장 유형의 남은 수가 0이면 다음 유형으로 이동 + while (grow[day][index] == 0) index++; + // 현재 위치에 성장 유형 인덱스만큼 높이 증가 + map[0][i] += index; + // 해당 성장 유형 사용 횟수 감소 + grow[day][index]--; + } + } + + // 나머지 위치의 성장 처리 함수 + private static void otherGrow() { + // 첫 번째 행을 제외한 나머지 행들을 처리 + for (int i = 1; i < m; i++) { + // 첫 번째 열을 제외한 나머지 열들을 처리 + for (int j = 1; j < m; j++) { + // 각 위치의 높이를 위쪽 경계(0행)의 같은 열 값으로 설정 + map[i][j] = map[0][j]; + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj15961.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj15961.java" new file mode 100644 index 00000000..ad1858e7 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj15961.java" @@ -0,0 +1,100 @@ +import java.io.*; +import java.util.*; +public class boj15961 { + // 입출력을 위한 버퍼 리더와 라이터 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 초밥 벨트 정보를 저장할 배열 + private static int[] sushi; + + // n: 접시의 수, d: 초밥의 가짓수, k: 연속해서 먹는 접시의 수, c: 쿠폰 번호 + private static int n, d, k, c; + + public static void main(String[] args) throws IOException { + // 입력 및 초기화 + init(); + + // 투 포인터 알고리즘으로 최대 초밥 가짓수 계산 + int answer = twoPointer(); + + // 결과 출력 및 리소스 정리 + bw.write(answer + ""); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받아 초기화하는 메소드 + */ + private static void init() throws IOException { + // 첫 번째 줄 입력: n, d, k, c + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 접시의 수 + d = Integer.parseInt(st.nextToken()); // 초밥의 가짓수 + k = Integer.parseInt(st.nextToken()); // 연속해서 먹는 접시의 수 + c = Integer.parseInt(st.nextToken()); // 쿠폰 번호 + + // 초밥 벨트 배열 초기화 + sushi = new int[n]; + + // 각 접시에 놓인 초밥 종류 입력 + for (int i = 0; i < n; i++) { + sushi[i] = Integer.parseInt(br.readLine()); + } + } + + /** + * 투 포인터 알고리즘을 사용하여 최대 초밥 가짓수를 찾는 메소드 + * @return 먹을 수 있는 초밥의 최대 가짓수 + */ + private static int twoPointer() { + int start = 0; // 시작 포인터 (현재 윈도우의 첫 접시) + int end = 0; // 끝 포인터 (다음에 추가할 접시) + int max = 0; // 최대 초밥 가짓수 + int cnt = 0; // 현재 윈도우에 있는 접시의 수 + + // 각 초밥 종류별 개수를 저장할 해시맵 + Map map = new HashMap<>(); + + // 시작 포인터가 전체 접시 수를 넘지 않을 때까지 반복 + while (start < n) { + // 현재 윈도우의 접시 수가 k보다 작으면 접시 추가 + if (cnt < k) { + // 현재 end 포인터 위치의 초밥을 해시맵에 추가 (또는 개수 증가) + map.put(sushi[end], map.getOrDefault(sushi[end], 0) + 1); + cnt++; // 접시 수 증가 + } + + // 현재 윈도우의 접시 수가 k가 되면 최대값 계산 후 시작 포인터 이동 + if (cnt == k) { + // 현재 윈도우에 있는 초밥 종류의 수 + int result = map.size(); + + // 쿠폰 초밥이 현재 윈도우에 없다면 가짓수 1 증가 + if (!map.containsKey(c)) { + result++; + } + + // 최대값 갱신 + max = Math.max(max, result); + + // 시작 포인터 위치의 초밥 제거 (또는 개수 감소) + map.put(sushi[start], map.get(sushi[start]) - 1); + cnt--; // 접시 수 감소 + + // 해당 초밥의 개수가 0이 되면 해시맵에서 제거 + if (map.get(sushi[start]) == 0) map.remove(sushi[start]); + + // 시작 포인터 이동 + start++; + } + + // 끝 포인터 이동 (원형 벨트이므로 모듈러 연산 사용) + end = (end + 1) % n; + } + + return max; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj18405.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj18405.java" new file mode 100644 index 00000000..1952aa6a --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj18405.java" @@ -0,0 +1,121 @@ +import java.io.*; +import java.util.*; + +public class boj18405 { + // 입출력을 위한 버퍼 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 상하좌우 이동을 위한 방향 배열 + private static final int[] dx = {1, 0, -1, 0}; // 아래, 오른쪽, 위, 왼쪽 + private static final int[] dy = {0, 1, 0, -1}; // 아래, 오른쪽, 위, 왼쪽 + + // 바이러스 정보를 저장할 리스트 (x좌표, y좌표, 바이러스 번호) + private static List virus; + + // 시험관 맵 정보를 저장할 2차원 배열 + private static int[][] map; + + // 문제에서 주어지는 입력값 + private static int n; // 시험관 크기 (N x N) + private static int k; // 바이러스 종류 + private static int s; // 경과 시간 + private static int x; // 확인할 위치 x좌표 + private static int y; // 확인할 위치 y좌표 + + public static void main(String[] args) throws IOException { + // 초기화 함수 호출 + init(); + + // BFS로 바이러스 확산 시뮬레이션 + BFS(); + + // 결과 출력 (x, y 위치의 바이러스 종류) + bw.write(map[x][y] + "\n"); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받아 초기 설정을 하는 함수 + */ + private static void init() throws IOException { + // 시험관 크기와 바이러스 종류 입력 받기 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + k = Integer.parseInt(st.nextToken()); + + // 맵과 바이러스 리스트 초기화 + map = new int[n][n]; + virus = new ArrayList<>(); + + // 시험관 정보 입력 받기 + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < n; j++) { + map[i][j] = Integer.parseInt(st.nextToken()); + + // 바이러스가 있는 위치라면 리스트에 추가 + if (map[i][j] != 0) { + virus.add(new int[]{i, j, map[i][j]}); + } + } + } + + // 바이러스 번호 기준으로 오름차순 정렬 (번호가 낮은 바이러스부터 증식해야 함) + virus.sort((o1, o2) -> o1[2] - o2[2]); + + // 시간과 확인할 위치 입력 받기 + st = new StringTokenizer(br.readLine()); + s = Integer.parseInt(st.nextToken()); // 경과 시간 + x = Integer.parseInt(st.nextToken()) - 1; // 행 번호 (0부터 시작하도록 -1) + y = Integer.parseInt(st.nextToken()) - 1; // 열 번호 (0부터 시작하도록 -1) + } + + /** + * BFS를 이용해 바이러스 확산을 시뮬레이션하는 함수 + */ + private static void BFS() { + Queue q = new ArrayDeque<>(); // BFS를 위한 큐 + + // 초기 바이러스 정보를 큐에 추가 (x좌표, y좌표, 바이러스 번호, 현재 시간) + for (int[] element : virus) { + q.add(new int[]{element[0], element[1], element[2], 0}); + } + + // BFS 시작 + while (!q.isEmpty()) { + int[] current = q.poll(); // 현재 처리할 바이러스 정보 + + // 목표 시간에 도달했으면 종료 + if (current[3] == s) return; + + // 상하좌우 네 방향으로 바이러스 확산 시도 + for (int i = 0; i < 4; i++) { + int nx = current[0] + dx[i]; // 새로운 x좌표 + int ny = current[1] + dy[i]; // 새로운 y좌표 + + // 범위를 벗어나거나 이미 바이러스가 있는 경우 건너뛰기 + if (OOB(nx, ny) || map[nx][ny] != 0) continue; + + // 바이러스 확산 + map[nx][ny] = current[2]; // 바이러스 번호로 맵 업데이트 + + // 다음 확산을 위해 큐에 추가 (시간 1 증가) + q.add(new int[]{nx, ny, current[2], current[3] + 1}); + } + } + } + + /** + * 주어진 좌표가 맵 범위를 벗어나는지 체크하는 함수 + * @param nx x좌표 + * @param ny y좌표 + * @return 범위를 벗어나면 true, 그렇지 않으면 false + */ + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > n - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj20166.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj20166.java" new file mode 100644 index 00000000..723f729c --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj20166.java" @@ -0,0 +1,108 @@ +import java.io.*; +import java.util.*; + +public class boj20166 { + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 8방향 이동을 위한 방향 배열 (대각선 포함) + private static final int[] dx = {1, 1, 1, 0, -1, -1, -1, 0}; + private static final int[] dy = {1, 0, -1, 1, 1, 0, -1, -1}; + + private static char[][] map; // 알파벳 격자 + private static String[] strings; // 찾을 문자열들 + private static Map stringCount; // 각 문자열이 등장하는 횟수 + private static Map memo; // 이미 방문한 상태를 저장하는 메모이제이션 + private static int n, m, k, maxLen; // n: 행 크기, m: 열 크기, k: 찾을 문자열 개수, maxLen: 찾을 문자열의 최대 길이 + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + + // 격자의 모든 위치에서 BFS 시작 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + BFS(i, j, map[i][j] + ""); // 현재 위치의 문자로 시작하는 문자열 탐색 + } + } + + // 결과 출력 + for (String s : strings) { + sb.append(stringCount.getOrDefault(s, 0)); // 각 문자열이 등장하는 횟수 출력 + sb.append("\n"); + } + + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + k = Integer.parseInt(st.nextToken()); + + map = new char[n][m]; + strings = new String[k]; + stringCount = new HashMap<>(); // 문자열 등장 횟수 저장 맵 + memo = new HashMap<>(); // 방문 상태 저장 맵 + + // 격자 입력 + for (int i = 0; i < n; i++) { + map[i] = br.readLine().toCharArray(); + } + + // 찾을 문자열 입력 + for (int i = 0; i < k; i++) { + strings[i] = br.readLine(); + maxLen = Math.max(maxLen, strings[i].length()); // 최대 길이 갱신 + } + } + + // 특정 위치에서 시작하는 모든 가능한 문자열을 탐색하는 BFS 메소드 + private static void BFS(int x, int y, String str) { + Queue q = new ArrayDeque<>(); + stringCount.put(str, stringCount.getOrDefault(str, 0) + 1); // 시작 문자열 카운트 증가 + q.add(new Node(x, y, str)); + + while(!q.isEmpty()) { + Node current = q.poll(); + + // 최대 길이에 도달하면 더 이상 확장하지 않음 + if (current.str.length() == maxLen) continue; + + // 8방향으로 탐색 + for (int i = 0; i < 8; i++) { + // 토러스 형태의 격자 (경계를 넘어가면 반대쪽으로 나옴) + int nx = (current.x + dx[i] + n) % n; + int ny = (current.y + dy[i] + m) % m; + + String next = current.str + map[nx][ny]; // 다음 문자열 + String memoKey = current.x + "," + current.y + "," + next; // 메모이제이션 키 + + // 길이 초과하거나 이미 방문한 상태면 스킵 + if (next.length() == maxLen + 1 || memo.containsKey(next)) continue; + + stringCount.put(next, stringCount.getOrDefault(next, 0) + 1); // 새 문자열 카운트 증가 + memo.put(memoKey, 1); // 방문 상태 저장 + q.add(new Node(nx, ny, next)); // 큐에 추가 + } + } + } + + // 탐색 상태를 저장하는 노드 클래스 + static class Node { + int x; // 현재 위치 x좌표 + int y; // 현재 위치 y좌표 + String str; // 현재까지 만든 문자열 + + Node(int x, int y, String str) { + this.x = x; + this.y = y; + this.str = str; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj2151.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj2151.java" new file mode 100644 index 00000000..e82c85cf --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj2151.java" @@ -0,0 +1,160 @@ +import java.io.*; +import java.util.*; +public class boj2151 { + // 입출력을 위한 버퍼 리더와 라이터 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 방향 상수 정의 + private static final int VERTICAL = 0; // 수직 방향(위아래) + private static final int HORIZONTAL = 1; // 수평 방향(좌우) + + // 출발지와 목적지 좌표를 저장할 리스트 + private static final List dest = new ArrayList<>(); + + // 맵 정보와 방문 여부를 저장할 배열 + private static char[][] home; // 집(맵) 정보 배열 + private static boolean[][] visited; // 방문 여부 배열 + private static int n; // 맵의 크기 + + /** + * 메인 메소드: 프로그램의 진입점 + */ + public static void main(String[] args) throws IOException { + // 입력 및 초기화 + init(); + + // 다익스트라 알고리즘으로 최단 경로 계산 + // 미러 설치 수는 경로 비용 - 1 (도착지까지 거울로 세기 때문에 -1) + int answer = dijkstra() - 1; + + // 결과 출력 및 리소스 정리 + bw.write(answer + ""); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받고 초기화하는 메소드 + */ + private static void init() throws IOException { + // 맵 크기 입력 + n = Integer.parseInt(br.readLine()); + + // 맵과 방문 배열 초기화 + home = new char[n][n]; + visited = new boolean[n][n]; + + // 맵 정보 입력 및 출발지/목적지 찾기 + for (int i = 0; i < n; i++) { + home[i] = br.readLine().toCharArray(); + for (int j = 0; j < n; j++) { + // '#' 표시가 있는 칸은 출발지와 목적지로 저장 + if (home[i][j] == '#') { + dest.add(new int[]{i, j}); + } + } + } + } + + /** + * 다익스트라 알고리즘으로 최단 경로를 찾는 메소드 + * 레이저가 목적지에 도달하기 위해 필요한 최소 미러 수를 계산 + * + * @return 필요한 노드 방문 횟수 (미러 수 + 1) + */ + private static int dijkstra() { + // 비용이 적은 노드부터 처리하기 위한 우선순위 큐 + PriorityQueue pq = new PriorityQueue<>((o1, o2) -> o1.cost - o2.cost); + + // 시작 노드(출발지) 방문 처리 및 큐에 추가 (두 방향 모두 큐에 추가) + visited[dest.get(0)[0]][dest.get(0)[1]] = true; + pq.add(new Node(dest.get(0)[0], dest.get(0)[1], 0, VERTICAL)); + pq.add(new Node(dest.get(0)[0], dest.get(0)[1], 0, HORIZONTAL)); + + // 큐가 빌 때까지 반복 + while (!pq.isEmpty()) { + // 현재 처리할 노드 꺼내기 + Node current = pq.poll(); + + // 목적지에 도달한 경우 현재까지의 비용 반환 + if (current.x == dest.get(1)[0] && current.y == dest.get(1)[1]) return current.cost; + + // 현재 방향에 따라 다음 위치 탐색 + if (current.dir == VERTICAL) { + // 수직 방향인 경우 - 아래쪽 탐색 + for (int i = current.x + 1; i < n; i++) { + // 벽을 만나면 더 이상 진행 불가 + if (home[i][current.y] == '*') break; + // 빈 공간이거나 이미 방문한 경우 스킵 + if (home[i][current.y] == '.' || visited[i][current.y]) continue; + // 방문 처리 및 큐에 추가 (방향 전환하여 수평으로) + visited[i][current.y] = true; + pq.add(new Node(i, current.y, current.cost + 1, HORIZONTAL)); + } + + // 수직 방향인 경우 - 위쪽 탐색 + for (int i = current.x - 1; i >= 0; i--) { + // 벽을 만나면 더 이상 진행 불가 + if (home[i][current.y] == '*') break; + // 빈 공간이거나 이미 방문한 경우 스킵 + if (home[i][current.y] == '.' || visited[i][current.y]) continue; + // 방문 처리 및 큐에 추가 (방향 전환하여 수평으로) + visited[i][current.y] = true; + pq.add(new Node(i, current.y, current.cost + 1, HORIZONTAL)); + } + } else { + // 수평 방향인 경우 - 오른쪽 탐색 + for (int i = current.y + 1; i < n; i++) { + // 벽을 만나면 더 이상 진행 불가 + if (home[current.x][i] == '*') break; + // 빈 공간이거나 이미 방문한 경우 스킵 + if (home[current.x][i] == '.' || visited[current.x][i]) continue; + // 방문 처리 및 큐에 추가 (방향 전환하여 수직으로) + visited[current.x][i] = true; + pq.add(new Node(current.x, i, current.cost + 1, VERTICAL)); + } + + // 수평 방향인 경우 - 왼쪽 탐색 + for (int i = current.y - 1; i >= 0; i--) { + // 벽을 만나면 더 이상 진행 불가 + if (home[current.x][i] == '*') break; + // 빈 공간이거나 이미 방문한 경우 스킵 + if (home[current.x][i] == '.' || visited[current.x][i]) continue; + // 방문 처리 및 큐에 추가 (방향 전환하여 수직으로) + visited[current.x][i] = true; + pq.add(new Node(current.x, i, current.cost + 1, VERTICAL)); + } + } + } + + // 목적지에 도달할 수 없는 경우 + return 0; + } + + /** + * 노드 클래스: 위치, 비용, 방향 정보를 저장 + */ + static class Node { + int x; // 행 좌표 + int y; // 열 좌표 + int cost; // 설치한 미러 수 + int dir; // 현재 방향 (VERTICAL 또는 HORIZONTAL) + + /** + * 노드 생성자 + * + * @param x 행 좌표 + * @param y 열 좌표 + * @param cost 현재까지의 비용(미러 수) + * @param dir 현재 방향 + */ + Node(int x, int y, int cost, int dir) { + this.x = x; + this.y = y; + this.cost = cost; + this.dir = dir; + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj4803.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj4803.java" new file mode 100644 index 00000000..04b0c590 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj4803.java" @@ -0,0 +1,126 @@ +import java.io.*; +import java.util.*; +public class boj4803 { + // 입출력을 위한 버퍼 리더와 라이터 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // 그래프 구조를 저장할 인접 리스트 + private static List> graph; + + // 노드 방문 여부를 저장할 배열 + private static boolean[] visited; + + // 사이클 존재 여부를 나타내는 플래그 + private static boolean isCycle; + + // n: 노드의 수, m: 간선의 수, answer: 트리의 수 + private static int n, m, answer; + + public static void main(String[] args) throws IOException { + // 테스트 케이스 번호 + int t = 1; + + // init() 함수가 false를 반환할 때까지 테스트 케이스 반복 + while (init()) { + // 모든 노드에 대해 방문하지 않은 노드부터 DFS 실행 + for (int i = 1; i <= n; i++) { + if (!visited[i]) { + // 사이클 플래그 초기화 + isCycle = false; + + // 연결 요소 발견, 트리 개수 증가 + answer++; + + // 시작 노드 방문 처리 + visited[i] = true; + + // DFS 실행 (parent = -1로 시작, 루트 노드는 부모가 없음) + DFS(i, -1); + + // 사이클이 발견되면 트리가 아니므로 개수 감소 + if (isCycle) answer--; + } + } + + // 결과 출력을 위한 문자열 구성 + sb.append("Case ").append(t++).append(": "); + if (answer == 0) sb.append("No trees."); + else if (answer == 1) sb.append("There is one tree."); + else sb.append("A forest of ").append(answer).append(" trees."); + sb.append("\n"); + } + + // 최종 결과 출력 및 리소스 정리 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + /** + * 입력을 받아 그래프를 초기화하는 메소드 + * @return 유효한 테스트 케이스인 경우 true, 종료 조건(n=0, m=0)인 경우 false + */ + private static boolean init() throws IOException { + // 노드 수와 간선 수 입력 + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); + m = Integer.parseInt(st.nextToken()); + + // 트리 개수 초기화 + answer = 0; + + // 종료 조건: n=0, m=0인 경우 + if (n == 0 && m == 0) return false; + + // 그래프 초기화 (인접 리스트) + graph = new ArrayList<>(); + + // 방문 배열 초기화 + visited = new boolean[n + 1]; + + // 그래프의 노드 리스트 초기화 (0번 인덱스는 사용하지 않음) + for (int i = 0; i <= n; i++) { + graph.add(new ArrayList<>()); + } + + // 간선 정보 입력 + for (int i = 0; i < m; i++) { + st = new StringTokenizer(br.readLine()); + int node1 = Integer.parseInt(st.nextToken()); + int node2 = Integer.parseInt(st.nextToken()); + + // 양방향 간선으로 그래프에 추가 + graph.get(node1).add(node2); + graph.get(node2).add(node1); + } + + return true; + } + + /** + * DFS를 수행하며 사이클 여부를 확인하는 메소드 + * @param current 현재 노드 + * @param parent 현재 노드의 부모 노드 + */ + private static void DFS(int current, int parent) { + // 현재 노드와 연결된 모든 노드 탐색 + for (int node : graph.get(current)) { + // 이미 방문한 노드이면서 부모 노드가 아닌 경우 => 사이클 발견 + if (visited[node] && node != parent) { + isCycle = true; + return; + } + // 아직 방문하지 않은 노드인 경우 + else if (!visited[node]) { + // 노드 방문 처리 + visited[node] = true; + + // 다음 노드로 DFS 재귀 호출 (현재 노드를 부모로 설정) + DFS(node, current); + } + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj7511.java" "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj7511.java" new file mode 100644 index 00000000..cf2820a2 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:8\354\243\274\354\260\250/boj7511.java" @@ -0,0 +1,84 @@ +import java.io.*; +import java.util.StringTokenizer; + +public class boj7511 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + private static int[] uf; // Union-Find를 위한 배열 + private static int n, k, m; // n: 유저 수, k: 관계 수, m: 쿼리 수 + + public static void main(String[] args) throws IOException { + int T = Integer.parseInt(br.readLine()); // 테스트 케이스 수 + + // 각 테스트 케이스 처리 + for (int tc = 1; tc <= T; tc++) { + sb.append("Scenario ").append(tc).append(":\n"); + init(); // 초기화 및 쿼리 처리 + + // 마지막 테스트 케이스가 아니라면 빈 줄 추가 + if (tc < T) { + sb.append("\n"); + } + } + + // 결과 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기화 및 쿼리 처리 메소드 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 유저 수 + k = Integer.parseInt(br.readLine()); // 친구 관계 수 + + // Union-Find 배열 초기화 + uf = new int[n]; + for (int i = 0; i < n; i++) { + uf[i] = i; // 각 노드는 처음에 자기 자신을 가리킴 + } + + // 친구 관계 입력 및 처리 + for (int i = 0; i < k; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int node1 = Integer.parseInt(st.nextToken()); + int node2 = Integer.parseInt(st.nextToken()); + + union(node1, node2); // 두 유저를 같은 집합으로 합침 + } + + // 쿼리 처리 + m = Integer.parseInt(br.readLine()); // 쿼리 수 + for (int i = 0; i < m; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + int node1 = Integer.parseInt(st.nextToken()); + int node2 = Integer.parseInt(st.nextToken()); + + // 두 유저가 같은 집합에 속하는지 확인 + if (find(node1) == find(node2)) { + sb.append(1); // 연결되어 있음 + } else { + sb.append(0); // 연결되어 있지 않음 + } + sb.append("\n"); + } + } + + // Union 연산: 두 집합을 합치는 메소드 + private static void union(int x, int y) { + int X = find(x); // x의 루트 찾기 + int Y = find(y); // y의 루트 찾기 + + uf[X] = Y; // x의 루트를 y의 루트로 변경 + } + + // Find 연산: 요소가 속한 집합의 루트를 찾는 메소드 (경로 압축 포함) + private static int find(int x) { + if (uf[x] == x) return x; // 자기 자신이 루트면 반환 + return uf[x] = find(uf[x]); // 경로 압축: 재귀적으로 루트를 찾고 바로 루트를 가리키도록 업데이트 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj1477.java" "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj1477.java" new file mode 100644 index 00000000..84d29b91 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj1477.java" @@ -0,0 +1,88 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.*; + +public class boj1477 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static int[] restArea; // 휴게소 위치를 저장하는 배열 + private static int n; + private static int m; + private static int max; // n: 현재 휴게소 개수, m: 추가할 휴게소 개수, l: 고속도로 길이, max: 휴게소 간 최대 거리 + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + int answer = binarySearch(); // 이진 탐색으로 최적 거리 찾기 + bw.write(String.valueOf(answer)); // 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 현재 휴게소 개수 + m = Integer.parseInt(st.nextToken()); // 추가할 휴게소 개수 + int l = Integer.parseInt(st.nextToken()); // 고속도로 길이 + max = 0; // 휴게소 간 최대 거리 초기화 + + restArea = new int[n + 2]; // 시작점(0)과 끝점(l)을 포함한 배열 + + st = new StringTokenizer(br.readLine()); + for (int i = 0; i < n; i++) { + restArea[i] = Integer.parseInt(st.nextToken()); // 현재 휴게소 위치 입력 + } + + restArea[n] = 0; // 시작점 추가 + restArea[n + 1] = l; // 끝점 추가 + + Arrays.sort(restArea); // 위치 순으로 정렬 + + // 휴게소 간 최대 거리 계산 + for (int i = 0; i < n + 1; i++) { + max = Math.max(max, restArea[i + 1] - restArea[i]); + } + } + + // 이진 탐색으로 최적 거리를 찾는 메소드 + private static int binarySearch() { + int start = 1; // 최소 거리는 1 + int end = max; // 최대 거리는 현재 휴게소 간 최대 거리 + int result = 0; // 결과 저장 변수 + + while (start <= end) { + int mid = start + (end - start) / 2; // 중간값 계산 + + if (isPossible(mid)) { // 현재 거리로 m개 이하의 휴게소를 추가할 수 있는지 확인 + result = mid; // 가능한 결과 저장 + end = mid - 1; // 더 작은 거리도 가능한지 확인 + } else { + start = mid + 1; // 더 큰 거리로 시도 + } + } + + return result; // 최종 결과 반환 + } + + // 주어진 거리로 m개 이하의 휴게소를 추가할 수 있는지 확인하는 메소드 + private static boolean isPossible(int distance) { + int count = 0; // 추가해야 할 휴게소 개수 + + for (int i = 0; i < n + 1; i++) { + int dist = restArea[i + 1] - restArea[i]; // 연속된 휴게소 간 거리 + + // 필요한 휴게소 개수 계산 + // dist / distance는 해당 구간에 필요한 총 휴게소 수 + // dist % distance == 0인 경우 경계선에 정확히 위치하므로 1개 빼줌 + count += (dist / distance) - (dist % distance == 0 ? 1 : 0); + } + + return count <= m; // m개 이하로 추가 가능한지 반환 + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17485.java" "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17485.java" new file mode 100644 index 00000000..c856091f --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17485.java" @@ -0,0 +1,87 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.*; + +public class Main { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // dp[i][j][k]: i행 j열에서 k방향으로 이동했을 때의 최소 비용 + // k=0: 오른쪽 대각선 아래, k=1: 바로 아래, k=2: 왼쪽 대각선 아래 + private static int[][][] dp; + + // 광산의 각 위치별 크기 정보 + private static int[][] arr; + + // 광산의 크기 + private static int n, m; + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + DP(); // 동적 프로그래밍으로 최소 비용 계산 + + // 마지막 행의 모든 위치에서 최소 비용 찾기 + int answer = (int)1e9; + for (int i = 0; i < m; i++) { + for (int j = 0; j < 3; j++) { + answer = Math.min(answer, dp[n - 1][i][j]); + } + } + + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 행 개수 + m = Integer.parseInt(st.nextToken()); // 열 개수 + + dp = new int[n][m][3]; // DP 배열 초기화 + arr = new int[n][m]; // 광산 정보 배열 초기화 + + // 광산 정보 입력 + for (int i = 0; i < n; i++) { + st = new StringTokenizer(br.readLine()); + for (int j = 0; j < m; j++) { + arr[i][j] = Integer.parseInt(st.nextToken()); + } + } + + // 첫 번째 행의 초기값 설정 + for (int i = 0; i < m; i++) { + for (int j = 0; j < 3; j++) { + dp[0][i][j] = arr[0][i]; + } + } + } + + // 동적 프로그래밍으로 최소 비용을 계산하는 메소드 + private static void DP() { + for (int i = 1; i < n; i++) { + // 첫 번째 열의 경우 + dp[i][0][2] = (int)1e9; // 왼쪽 대각선 아래로 이동 불가능 + dp[i][0][1] = arr[i][0] + dp[i - 1][0][0]; // 바로 아래로 이동 + dp[i][0][0] = arr[i][0] + Math.min(dp[i - 1][1][1], dp[i - 1][1][2]); // 오른쪽 대각선 아래로 이동 + + // 중간 열들의 경우 + for (int j = 1; j < m - 1; j++) { + dp[i][j][0] = arr[i][j] + Math.min(dp[i - 1][j + 1][1], dp[i - 1][j + 1][2]); // 오른쪽 대각선 아래 + dp[i][j][1] = arr[i][j] + Math.min(dp[i - 1][j][0], dp[i - 1][j][2]); // 바로 아래 + dp[i][j][2] = arr[i][j] + Math.min(dp[i - 1][j - 1][0], dp[i - 1][j - 1][1]); // 왼쪽 대각선 아래 + } + + // 마지막 열의 경우 + dp[i][m - 1][0] = (int)1e9; // 오른쪽 대각선 아래로 이동 불가능 + dp[i][m - 1][1] = arr[i][m - 1] + dp[i - 1][m - 1][2]; // 바로 아래로 이동 + dp[i][m - 1][2] = arr[i][m - 1] + Math.min(dp[i - 1][m - 2][0], dp[i - 1][m - 2][1]); // 왼쪽 대각선 아래로 이동 + } + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17779.java" "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17779.java" new file mode 100644 index 00000000..7657ffb8 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj17779.java" @@ -0,0 +1,171 @@ +import java.io.*; +import java.util.*; + +public class boj17779 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + private static int[][] map; // 원본 구역의 인구 정보를 저장하는 배열 + private static int[][] arr; // 구역 번호를 표시하는 배열 + private static int n, sum; // n: 맵의 크기, sum: 전체 인구 수 + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + + int answer = separate(); // 선거구 나누기 + bw.write(String.valueOf(answer)); // 결과 출력 + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + n = Integer.parseInt(br.readLine()); // 맵의 크기 입력 + sum = 0; // 전체 인구 수 초기화 + + map = new int[n + 1][n + 1]; // 1-indexed 인구 정보 배열 + arr = new int[n + 1][n + 1]; // 구역 번호 표시 배열 + + // 인구 정보 입력 + for (int i = 1; i <= n; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + for (int j = 1; j <= n; j++) { + map[i][j] = Integer.parseInt(st.nextToken()); + sum += map[i][j]; // 전체 인구 수 누적 + } + } + } + + // 선거구를 나누고 인구 차이의 최솟값을 계산하는 메소드 + private static int separate() { + int min = sum; // 인구 차이 최솟값 (초기값은 최대값으로) + + // 기준점 (x, y)와 경계의 길이 d1, d2를 모든 가능한 조합으로 탐색 + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + for (int d1 = 1; d1 <= n; d1++) { + for (int d2 = 1; d2 <= n; d2++) { + // 경계가 맵 내부에 위치하는지 확인 + if (1 <= i && i + d1 + d2 <= n && j + d2 <= n && 1 <= j - d1) { + // 각 선거구의 인구 수 계산 + int one = oneArea(i, j, d1, d2); // 1번 선거구 + int two = twoArea(i, j, d1, d2); // 2번 선거구 + int three = threeArea(i, j, d1, d2); // 3번 선거구 + int four = fourArea(i, j, d1, d2); // 4번 선거구 + int five = sum - (one + two + three + four); // 5번 선거구 + + // 최대 인구와 최소 인구의 차이 계산 및 최솟값 갱신 + min = Math.min(min, max(one, two, three, four, five) - min(one, two, three, four, five)); + } + } + } + } + } + return min; + } + + // 5개 값 중 최댓값을 반환하는 메소드 + private static int max(int one, int two, int three, int four, int five) { + int max = one; + if (two > max) max = two; + if (three > max) max = three; + if (four > max) max = four; + if (five > max) max = five; + return max; + } + + // 5개 값 중 최솟값을 반환하는 메소드 + private static int min(int one, int two, int three, int four, int five) { + int min = one; + if (two < min) min = two; + if (three < min) min = three; + if (four < min) min = four; + if (five < min) min = five; + return min; + } + + // 1번 선거구의 인구 수를 계산하는 메소드 + private static int oneArea(int x, int y, int d1, int d2) { + int result = 0; + + // 1번 선거구의 윗부분 + for (int i = 1; i < x; i++) { + for (int j = 1; j <= y; j++) { + result += map[i][j]; + arr[i][j] = 1; + } + } + + // 1번 선거구의 아랫부분 (경계선 고려) + int temp = 1; + for (int i = x; i < x + d1; i++) { + for (int j = 1; j <= y - temp; j++) { + result += map[i][j]; + arr[i][j] = 1; + } + temp++; + } + return result; + } + + // 2번 선거구의 인구 수를 계산하는 메소드 + private static int twoArea(int x, int y, int d1, int d2) { + int result = 0; + + // 2번 선거구의 윗부분 + for (int i = 1; i <= x; i++) { + for (int j = y + 1; j <= n; j++) { + result += map[i][j]; + arr[i][j] = 2; + } + } + + // 2번 선거구의 아랫부분 (경계선 고려) + int temp = 2; + for (int i = x + 1; i <= x + d2; i++) { + for (int j = y + temp; j <= n; j++) { + result += map[i][j]; + arr[i][j] = 2; + } + temp++; + } + + return result; + } + + // 3번 선거구의 인구 수를 계산하는 메소드 + private static int threeArea(int x, int y, int d1, int d2) { + int result = 0; + + // 3번 선거구 (경계선 고려) + int temp = 0; + for (int i = x + d1; i <= n; i++) { + for (int j = 1; j < y - d1 + temp; j++) { + result += map[i][j]; + arr[i][j] = 3; + } + if (i < x + d1 + d2) temp++; + } + + return result; + } + + // 4번 선거구의 인구 수를 계산하는 메소드 + private static int fourArea(int x, int y, int d1, int d2) { + int result = 0; + + // 4번 선거구 (경계선 고려) + int temp = 0; + for (int i = x + d2 + 1; i <= n; i++) { + for (int j = y + d2 - temp; j <= n; j++) { + result += map[i][j]; + arr[i][j] = 4; + } + if (i <= x + d1 + d2) temp++; + } + + return result; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj2186.java" "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj2186.java" new file mode 100644 index 00000000..a2ad6b69 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj2186.java" @@ -0,0 +1,115 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.*; + +public class boj2186 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + + // 상하좌우 이동을 위한 방향 배열 + private static final int[] dx = {1, 0, -1, 0}; + private static final int[] dy = {0, 1, 0, -1}; + + // BFS를 위한 큐 + private static Queue q; + + // 보드 정보 저장 + private static char[][] map; + + // 동적 프로그래밍을 위한 3차원 배열 + // dp[i][j][l]: (i,j) 위치에서 target의 l번째 문자에 도달하는 경우의 수 + private static int[][][] dp; + + // 목표 문자열 + private static char[] target; + + // 보드 크기와 점프 거리 + private static int n, m, k; + + public static void main(String[] args) throws IOException { + init(); // 초기 데이터 설정 + BFS(); // BFS로 경우의 수 계산 + + // 최종 결과 계산 (시작 문자에 도달하는 모든 경우의 수 합산) + int answer = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + answer += dp[i][j][0]; + } + } + + bw.write(String.valueOf(answer)); + bw.flush(); + bw.close(); + br.close(); + } + + // 초기 데이터를 설정하는 메소드 + private static void init() throws IOException { + StringTokenizer st = new StringTokenizer(br.readLine()); + n = Integer.parseInt(st.nextToken()); // 보드 행 크기 + m = Integer.parseInt(st.nextToken()); // 보드 열 크기 + k = Integer.parseInt(st.nextToken()); // 최대 점프 거리 + + map = new char[n][m]; // 보드 배열 + dp = new int[n][m][101]; // DP 배열 (문자열 최대 길이가 100이므로) + + // 보드 정보 입력 + for (int i = 0; i < n; i++) { + map[i] = br.readLine().toCharArray(); + } + + // 목표 문자열 입력 + target = br.readLine().toCharArray(); + + q = new ArrayDeque<>(); // BFS 큐 초기화 + + // 목표 문자열의 마지막 문자와 일치하는 모든 위치를 큐에 추가 + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (map[i][j] == target[target.length - 1]) { + q.add(new int[]{i, j, target.length - 1}); + dp[i][j][target.length - 1] = 1; // 마지막 문자 위치에서 시작하는 경우의 수는 1 + } + } + } + } + + // BFS로 가능한 경로의 수를 계산하는 메소드 + private static void BFS() { + while (!q.isEmpty()) { + int[] current = q.poll(); + // 이미 첫 번째 문자에 도달했으면 더 이상 진행하지 않음 + if (current[2] == 0) continue; + + // 네 방향으로 탐색 + for (int i = 0; i < 4; i++) { + // 1부터 k까지의 거리만큼 점프 + for (int j = 1; j <= k; j++) { + int nx = current[0] + dx[i] * j; + int ny = current[1] + dy[i] * j; + + // 경계를 벗어나거나 이전 문자와 일치하지 않으면 스킵 + if (OOB(nx, ny) || map[nx][ny] != target[current[2] - 1]) continue; + + // 현재 위치에서 다음 위치로 가는 경우의 수 누적 + dp[nx][ny][current[2] - 1] += dp[current[0]][current[1]][current[2]]; + + // 이전에 방문하지 않았던 상태라면 큐에 추가 + if (dp[nx][ny][current[2] - 1] == dp[current[0]][current[1]][current[2]]) { + q.add(new int[]{nx, ny, current[2] - 1}); + } + } + } + } + } + + // 배열 범위를 벗어났는지 확인하는 메소드 + private static boolean OOB(int nx, int ny) { + return nx < 0 || nx > n - 1 || ny < 0 || ny > m - 1; + } +} \ No newline at end of file diff --git "a/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj4195.java" "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj4195.java" new file mode 100644 index 00000000..43934c87 --- /dev/null +++ "b/\355\225\234\354\242\205\354\232\261:9\354\243\274\354\260\250/boj4195.java" @@ -0,0 +1,91 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.*; + +public class boj4195 { + // 입출력을 위한 BufferedReader, BufferedWriter 선언 + private static final BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); + private static final StringBuilder sb = new StringBuilder(); + + // Union-Find를 위한 맵들 + private static Map map; // 부모 노드를 저장하는 맵 (parent) + private static Map size; // 각 집합의 크기를 저장하는 맵 + + private static int f; // 친구 관계의 수 + + public static void main(String[] args) throws IOException { + int t = Integer.parseInt(br.readLine()); // 테스트 케이스 수 + + // 각 테스트 케이스 처리 + for (int i = 0; i < t; i++) { + init(); // 초기화 및 친구 관계 처리 + } + + // 결과 출력 + bw.write(sb.toString()); + bw.flush(); + bw.close(); + br.close(); + } + + // 각 테스트 케이스의 초기화 및 처리 + private static void init() throws IOException { + f = Integer.parseInt(br.readLine()); // 친구 관계의 수 + map = new HashMap<>(); // 부모 노드 맵 초기화 + size = new HashMap<>(); // 집합 크기 맵 초기화 + + // 각 친구 관계 처리 + for (int i = 0; i < f; i++) { + StringTokenizer st = new StringTokenizer(br.readLine()); + String name1 = st.nextToken(); // 첫 번째 친구 이름 + String name2 = st.nextToken(); // 두 번째 친구 이름 + + // 처음 등장하는 이름이면 자기 자신을 부모로 초기화 + if (!map.containsKey(name1)) map.put(name1, name1); + if (!map.containsKey(name2)) map.put(name2, name2); + + // 처음 등장하는 이름이면 집합 크기를 1로 초기화 + if (!size.containsKey(name1)) size.put(name1, 1); + if (!size.containsKey(name2)) size.put(name2, 1); + + // 두 친구를 같은 집합으로 합침 + union(name1, name2); + + // 합친 후의 집합 크기 출력 + sb.append(Math.max(size.get(find(name1)), size.get(find(name2)))).append("\n"); + } + } + + // 두 집합을 합치는 메소드 + private static void union(String name1, String name2) { + String N1 = find(name1); // name1의 루트 찾기 + String N2 = find(name2); // name2의 루트 찾기 + + // 이미 같은 집합이면 합치지 않음 + if (N1.equals(N2)) return; + + // 더 작은 집합을 더 큰 집합에 합침 (최적화) + if (size.get(N1) < size.get(N2)) { + map.put(N1, N2); // N1의 부모를 N2로 설정 + size.put(N2, size.get(N2) + size.get(N1)); // N2 집합의 크기 증가 + } else { + map.put(N2, N1); // N2의 부모를 N1으로 설정 + size.put(N1, size.get(N2) + size.get(N1)); // N1 집합의 크기 증가 + } + } + + // 경로 압축을 사용하여 요소의 루트를 찾는 메소드 + private static String find(String x) { + if (map.get(x).equals(x)) { + return x; // 자기 자신이 루트인 경우 + } else { + String parent = find(map.get(x)); // 재귀적으로 루트 찾기 + map.put(x, parent); // 경로 압축: 루트를 바로 가리키도록 설정 + return parent; + } + } +} \ No newline at end of file