Happy Number - LeetCode

 

주어진 수 n(1 ≤ n ≤ 2^31 -1)이 happy number라면 true를 반환하라.

happy number는 다음의 과정을 거친다.

  1. 임의의 양의 정수로 시작하여, 숫자를 그 숫자의 각 자릿수의 제곱의 합으로 교체한다.
  2. 숫자가 1이 될 때까지 또는 1을 포함하지 않은 사이클에서 무한히 반복
  3. 이 과정이 1로 끝나는 숫자를 happy number라고 한다.

만약 n = 19 라면 19 → 82 → 68 → 100 → 1 그러므로 true

무한 루프에 빠졌는지 판단이 필요.

무한 루프에 빠졌다는 것은 최초의 상태로 돌아왔다는 뜻이다.

n = 20 이라면 20 → 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20

값들을 캐싱하고 계산 결과가 캐시에 존재한다면 무한루프가 존재한다는 것이다.

class Solution {
    public boolean isHappy(int n) {
        Set<Long> cache = new HashSet<>();
        
        long num = n;
        while (num != 1) {
            if (cache.contains(num)) return false;
            cache.add(num);
            num = calc(num);
        }
        
        return true;
    }

    private long calc(long num) {
        long sum = 0;
        
        while (num != 0) {
            sum += Math.pow(num % 10, 2);
            num /= 10;
        }
        return sum;
    }
}

'Algorithm > LeetCode' 카테고리의 다른 글

LeetCode 322 - Coin Change  (1) 2024.11.03
LeetCode 155 - Min Stack  (0) 2024.11.02
LeetCode 74 - Search a 2D Matrix  (1) 2024.10.30
LeetCode 66 - Plus One  (0) 2024.10.25
LeetCode 198 - House Robber  (0) 2024.10.25

11657번: 타임머신

 

N(1≤N≤500) 개의 도시, 버스가 M(1≤M≤6,000) 개

각 버스는 A, B, C로 나타낼 수 있는데 A는 시작도시, B는 도착도시, C는 버스를 타고 걸리는 데 걸리는 시간

-10,000 ≤ C ≤ 10,000

1번 도시에서 출발해 나머지 도시로 가는 가장 빠른 시간은?

음수가 존재하는 최단거리이다. 출발 도착이 존재하기 때문에 방향 그래프이다.

1번 도시에서 출발해 어떤 도시로 가는 과정에서 시간을 무한히 오래전으로 되돌릴 수 있다면 -1을 출력

⇒ 1번에서 출발해서 순환이 존재 + 비용의 합이 음수 = 무한히 오래 전으로 돌릴 수 있다.

도달할 수 없다 = 경로가 없다.

벨만-포드를 통해 음수 가중치를 처리 + 음수 사이클 감지

package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class BOJ11657 {
	static class Edge {
		int from;
		int to;
		int cost;

		Edge(int from, int to, int cost) {
			this.from = from;
			this.to = to;
			this.cost = cost;
		}
	}

	static FastReader scan = new FastReader();
	static int N, M;
	static Edge[] edges;

	static void input() {
		N = scan.nextInt();
		M = scan.nextInt();
		edges = new Edge[M];

		for (int i = 0; i < M; i++) {
			edges[i] = new Edge(scan.nextInt(), scan.nextInt(), scan.nextInt());
		}
	}

	static void solve() {
		long[] dist = new long[N + 1];
		Arrays.fill(dist, Integer.MAX_VALUE);
		dist[1] = 0;

		boolean hasNegativeCycle = false;

		for (int i = 1; i <= N; i++) {
			for (Edge edge : edges) {
				if (dist[edge.from] != Integer.MAX_VALUE &&
					dist[edge.to] > dist[edge.from] + edge.cost) {
					dist[edge.to] = dist[edge.from] + edge.cost;

					if (i == N) {
						// N인데도 갱신? -> 음수 사이클 존재
						hasNegativeCycle = true;
						break;
					}
				}
			}
		}

		StringBuilder sb = new StringBuilder();
		if (hasNegativeCycle) {
			System.out.println(-1);
		} else {
			for (int i = 2; i <= N; i++) {
				if (dist[i] == Integer.MAX_VALUE) {
					sb.append(-1).append('\\n');
				} else {
					sb.append(dist[i]).append('\\n');
				}
			}
			System.out.println(sb);
		}
	}

	public static void main(String[] args) {
		input();
		solve();
	}

	static class FastReader {
		BufferedReader br;
		StringTokenizer st;

		public FastReader() {
			br = new BufferedReader(new InputStreamReader(System.in));
		}

		String next() {
			while (st == null || !st.hasMoreElements()) {
				try {
					st = new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			return st.nextToken();
		}

		int nextInt() {
			return Integer.parseInt(next());
		}

		long nextLong() {
			return Long.parseLong(next());
		}

		String nextLine() {
			String str = "";
			try {
				str = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return str;
		}
	}
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 9370 - 미확인 도착지  (1) 2024.11.15
BOJ 10942 - 팰린드롬?  (0) 2024.11.12
BOJ 4991 - 로봇 청소기  (0) 2024.11.11
BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 6087 - 레이저 통신  (0) 2024.11.07

9370번: 미확인 도착지

 

출발지 s, 목적지 후보 존재. 목적지까지 최단거리로 갈 것이다.

g와 h 사이 경로를 지난다.

목적지 후보 중 불가능한 경우를 제외한 목적지들을 공백으로 분리시킨 오름차순 정수들로 출력

목적지 후보들 까지 가는데 최단거리로 간다. 이 과정에서 g와 h 사이의 간선을 지나는 경로가 존재한다면 해당 목적지 후보는 가능한 목적지이다.

  1. 다익스트라를 통해 최단거리 경로를 찾는다.
  2. 목적지 후보까지 최단거리 경로에 g, h 사이 경로가 포함된다면 정답에 추가

이 방법은 틀린다. 이거는 그냥 다익스트라로 구한 최단 경로에 우연히 g-h 사이 경로가 포함 돼 있는지 확인할 뿐이다.

  1. 다익스트라를 통해 최단 거리를 구한다.
  2. s → g → h → 도착, s → h → g → 도착지 이 경로들 중 하나라도 최단 거리와 같은 거리라면? 그 도착지는 가능한 도착지이다.
    1. s → g 까지 최단거리 + g - h 거리 + h → 도착지 최단거리
    2. s → h 까지 최단거리 + g - h 거리 + h → 도착지 최단거리
package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
import java.util.stream.Collectors;

public class BOJ9370 {
    static class Node {
        int id;
        int distance;

        public Node(int id, int distance) {
            this.id = id;
            this.distance = distance;
        }
    }

    static int n, m, t;
    static int s, g, h;
    static List<List<Node>> graph;
    static int[] goalCandidates;
    static List<Integer> result;
    static int gh;
    static FastReader scan = new FastReader();

    static void solve() {
        int[] fromStart = dijkstra(s);
        int[] fromG = dijkstra(g);
        int[] fromH = dijkstra(h);

        for (int goal : goalCandidates) {
            int normalDist = fromStart[goal];

            int routeOne = fromStart[g] + gh + fromH[goal];
            int routeTwo = fromStart[h] + gh + fromG[goal];

            if (Math.min(routeOne, routeTwo) == normalDist) {
                result.add(goal + 1);
            }
        }
        printResult();
    }

    static int[] dijkstra(int start) {
        int[] distances = new int[n];
        PriorityQueue<Node> pq = new PriorityQueue<>(Comparator.comparingInt(n -> n.distance));

        for (int node = 0; node < n; node++) {
            distances[node] = Integer.MAX_VALUE;
        }
        distances[start] = 0;
        pq.add(new Node(start, 0));

        while (!pq.isEmpty()) {
            Node curr = pq.poll();

            if (curr.distance > distances[curr.id]) {
                continue;
            }

            for (Node neighbor : graph.get(curr.id)) {
                int newDistance = distances[curr.id] + neighbor.distance;

                if (newDistance < distances[neighbor.id]) {
                    distances[neighbor.id] = newDistance;
                    pq.offer(new Node(neighbor.id, newDistance));
                }
            }
        }

        return distances;
    }

    static void printResult() {
        String answer = result.stream().sorted().map(String::valueOf).collect(Collectors.joining(" "));
        System.out.println(answer);
    }

    public static void main(String[] args) {
        int T = scan.nextInt();
        for (int i = 0; i < T; i++) {
            graph = new ArrayList<>();
            result = new ArrayList<>();

            n = scan.nextInt();
            m = scan.nextInt();
            t = scan.nextInt();
            s = scan.nextInt() - 1;
            g = scan.nextInt() - 1;
            h = scan.nextInt() - 1;

            goalCandidates = new int[t];
            for (int j = 0; j < n; j++) {
                graph.add(new ArrayList<>());
            }

            for (int j = 0; j < m; j++) {
                int node1 = scan.nextInt() - 1;
                int node2 = scan.nextInt() - 1;
                int cost = scan.nextInt();
                graph.get(node1).add(new Node(node2, cost));
                graph.get(node2).add(new Node(node1, cost));

                if ((node1 == g && node2 == h) || (node1 == h && node2 == g)) {
                    gh = cost;
                }
            }

            for (int j = 0; j < t; j++) {
                goalCandidates[j] = scan.nextInt() - 1;
            }

            solve();
        }
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 11657 - 타임머신  (0) 2024.11.21
BOJ 10942 - 팰린드롬?  (0) 2024.11.12
BOJ 4991 - 로봇 청소기  (0) 2024.11.11
BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 6087 - 레이저 통신  (0) 2024.11.07

10942번: 팰린드롬?

 

N개의 자연수, M개의 질문

질문은 S E 형태. S번째 수부터 E번째 까지 수가 팰린드롬을 이루는가? → 이루면 1 아니면 0

(1≤N≤2,000, 자연수 ≤ 100,000, 1≤ M ≤ 1,000,000)

M개의 질문에 대해 매번 팰린드롬 여부를 판단하는 것은 시간초과이다.

답을 구해 놓고 쿼리를 순회하며 답을 출력해야 할 것 같다.

package boj;

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

public class BOJ10942 {
    static int N, M;
    static int[] nums;
    static int[][] queries;
    static FastReader scan = new FastReader();
    static boolean[][] dp;

    static void input() {
        N = scan.nextInt();
        nums = new int[N];
        for (int i = 0; i < N; i++) {
            nums[i] = scan.nextInt();
        }
        M = scan.nextInt();
        queries = new int[M][2];
        for (int i = 0; i < M; i++) {
            queries[i] = new int[]{scan.nextInt(), scan.nextInt()};
        }
        dp = new boolean[N][N];
    }

    static void solve() {
        for (int s = 0; s < N; s++) {
            // 홀수개 팰린드롬
            checkPalindrome(s, s);
            // 짝수개 팰린드롬
            checkPalindrome(s, s + 1);
        }

        StringBuilder sb = new StringBuilder();
        for (int[] query : queries) {
            int s = query[0] - 1;
            int e = query[1] - 1;
            boolean result = dp[s][e];
            sb.append(result == true ? 1 : 0).append("\\n");
        }
        System.out.println(sb);
    }

    static void checkPalindrome(int l, int r) {
        while (l >= 0 && r < N) {
            if (nums[l] == nums[r]) {
                dp[l][r] = true;
                l--;
                r++;
            } else {
                return;
            }
        }
    }

    public static void main(String[] args) {
        input();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 11657 - 타임머신  (0) 2024.11.21
BOJ 9370 - 미확인 도착지  (1) 2024.11.15
BOJ 4991 - 로봇 청소기  (0) 2024.11.11
BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 6087 - 레이저 통신  (0) 2024.11.07

4991번: 로봇 청소기

 

방의 정보가 주어졌을 때, 더러운 칸을 모두 깨끗한 칸으로 만드는데 필요한 이동 횟수의 최솟값

우선 같은 칸을 여러 번 방문할 수 있다는 점을 주의해야 한다.

즉 노드를 기준으로 방문처리를 하면 안 된다. 그럼 어떻게 방문 처리를 할까?

간선을 만들고 이때 모든 점을 연결하지 못한다면 -1이다.

모든 지점 간의 거리를 계산해서 간선과 코스를 구한다.

노드 간의 최단거리 즉 간선을 어떻게 구할까? → 노드별로 bfs를 통해서 처리한다.

그 후 어떤 방식으로 간선을 선택할까?

크루스칼 알고리즘이 떠오른다. 하지만 이 문제는 출발점이 명확하고 크루스칼로 가장 비용이 적은 그래프를 만들어도 왔다 갔다 하는 경우가 발생해 최소 비용이 안된다.

결국 모든 경우의 수를 다 따져야 한다.

  1. 노드 간 최단 거리를 구해 저장한다.
  2. 더러운 칸 노드를 순열로 순서를 정한다.
  3. 시작점부터 순서대로 이동하는 거리의 합을 구해 최솟값을 갱신한다.
package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;

public class BOJ4991 {

    static FastReader scan = new FastReader();
    static final int[][] DIRECTION = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    static int W, H;
    static int[][] map;
    static int[] start;
    static Map<Integer, int[]> nodes;
    static int MIN;

    static int[][] calculateDistance() {
        int n = nodes.size();
        int[][] distances = new int[n + 1][n + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(distances[i], -1);
        }
        for (int start = 1; start <= nodes.size(); start++) {
            Queue<int[]> q = new LinkedList<>();
            boolean[][] visited = new boolean[H][W];
            int[] node = nodes.get(start);
            q.add(new int[]{node[0], node[1], 0});
            visited[node[0]][node[1]] = true;

            while (!q.isEmpty()) {
                int[] curr = q.poll();
                int r = curr[0];
                int c = curr[1];
                int cost = curr[2];

                if (map[r][c] > 0) {
                    distances[start][map[r][c]] = cost;
                    distances[map[r][c]][start] = cost;
                }

                for (int d = 0; d < 4; d++) {
                    int nr = r + DIRECTION[d][0];
                    int nc = c + DIRECTION[d][1];

                    if (!inRange(nr, nc)) continue;
                    if (map[nr][nc] == -1) continue;
                    if (visited[nr][nc]) continue;

                    q.add(new int[]{nr, nc, cost + 1});
                    visited[nr][nc] = true;
                }
            }
        }

        return distances;
    }

    static boolean inRange(int r, int c) {
        return r >= 0 && r < H && c >= 0 && c < W;
    }

    static void solve() {
        int[][] distances = calculateDistance();
        int n = nodes.size();
        permutate(0, n - 1, new int[n - 1], new boolean[n + 1], distances);
        System.out.println(MIN == Integer.MAX_VALUE ? -1 : MIN);
    }

    static void permutate(int depth, int n, int[] selected, boolean[] visited, int[][] distances) {
        if (depth == n) {
            int distance = 0;
            int start = 1;
            for (int node : selected) {
                if (distances[start][node] == -1) {
                    return;
                }
                distance += distances[start][node];
                start = node;
            }
            MIN = Math.min(MIN, distance);
            return;
        }

        for (int i = 2; i <= n + 1; i++) {
            if (visited[i]) continue;
            selected[depth] = i;
            visited[i] = true;
            permutate(depth + 1, n, selected, visited, distances);
            selected[depth] = 0;
            visited[i] = false;
        }
    }

    public static void main(String[] args) {
        while (true) {
            MIN  = Integer.MAX_VALUE;
            W = scan.nextInt();
            H = scan.nextInt();
            if (W == 0 && H == 0) {
                return;
            }
            map = new int[H][W];
            nodes = new HashMap<>();
            int id = 1;
            for (int i = 0; i < H; i++) {
                char[] row = scan.next().toCharArray();
                for (int j = 0; j < W; j++) {
                    if (row[j] == 'o') {
                        map[i][j] = 1; // 시작점
                        start = new int[]{i, j};
                        nodes.put(1, start);
                    } else if (row[j] == '*') {
                        map[i][j] = ++id; // 더러운 칸 노드
                        nodes.put(id, new int[]{i, j});
                    } else if (row[j] == 'x') {
                        map[i][j] = -1; // 이동 불가능 칸
                    }
                }
            }

            solve();
        }
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        String nextLine() {
            String str = "";
            try {
                str = br.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 9370 - 미확인 도착지  (1) 2024.11.15
BOJ 10942 - 팰린드롬?  (0) 2024.11.12
BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 6087 - 레이저 통신  (0) 2024.11.07
BOJ 10021 - Watering the Fields  (0) 2024.11.06

11049번: 행렬 곱셈 순서

 

N(1≤N≤500) 개의 행렬이 주어졌을 때 어떤 순서로 곱셈을 해야 연산 횟수를 최소화할 수 있을까?

예를 들어 A가 5x3이고 B가 3x2, C가 2x6인 경우를 생각해보자.

우선 (AB) C는 5x2 AB를 만들기 위해 5x3x2의 연산 횟수가 필요하고 5x2 AB와 2x6C가 곱해져 최종적으로 5x6이 나오는데 여기에 5x2x6의 연산 횟수가 필요하다.

최종적으로 5x3x2 + 5x2x6 = 90번의 연산이다.

그런데 이 연산을 A(BC)로 하면 3x2x6 + 5x3x6 = 126이 된다. 같은 곱셈이지만 곱셈 연산 수가 다르다.

우선 완전 탐색을 고려할 수 있다. 하지만 너무 많다..

생각해 보면 ABCD가 있을 때 (AB)(CD)가 있을 수 있고 ((AB) C) D가 있을 수 있다. AB가 반복해서 나온다. 즉 DP를 사용하면 될 것 같다.

상태를 생각해 보자. i 번째까지의 최소 연산 횟수를 저장하면 될까?

아니다. 앞에서부터 계산한다고 결과가 최소가 되지 않는다.

i부터 j까지의 연산을 기록하자..

그렇다면 dp[i][j]를 어떻게 정의할까?

dp[i][j]: i 번째 행렬부터 j번째 행렬까지 곱하는데 필요한 최소 연산 횟수

dp[i][j] = min(dp[i][k] + dp[k+1][j] + (i x k x j))가 된다.

package boj;

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

public class BOJ11049 {

    static FastReader scan = new FastReader();
    static int N;
    static int[][] matrices;

    static void input() {
        N = scan.nextInt();
        matrices = new int[N][2];
        for (int i = 0; i < N; i++) {
            matrices[i] = new int[]{scan.nextInt(), scan.nextInt()};
        }
    }

    static void solve() {
        int[][] dp = new int[N][N];
        for (int i = 0; i < N; i++) {
            Arrays.fill(dp[i], -1);
        }

        System.out.println(recFunc(0, N - 1, dp));
    }

    static int recFunc(int start, int end, int[][] dp) {
        if (start == end) {
            return 0;
        }

        if (dp[start][end] != -1) {
            return dp[start][end];
        }

        dp[start][end] = Integer.MAX_VALUE;
        for (int mid = start; mid < end; mid++) {
            int leftCost = recFunc(start, mid, dp);
            int rightCost = recFunc(mid + 1, end, dp);
            int mergeCost = matrices[start][0] * matrices[mid][1] * matrices[end][1];

            dp[start][end] = Math.min(dp[start][end], leftCost + rightCost + mergeCost);
        }
        return dp[start][end];
    }

    public static void main(String[] args) {
        input();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 10942 - 팰린드롬?  (0) 2024.11.12
BOJ 4991 - 로봇 청소기  (0) 2024.11.11
BOJ 6087 - 레이저 통신  (0) 2024.11.07
BOJ 10021 - Watering the Fields  (0) 2024.11.06
BOJ 2292 - 벌집  (5) 2024.11.06

6087번: 레이저 통신

 

c 끼리 레이저로 통신하기 위해 설치해야 하는 거울 개수의 최솟값.

‘/’, ‘\’을 설치해 방향을 90도 회전할 수 있다.

시작점과 도착점이 존재하며 두 점 중 어느 점에서 레이저를 쏴도 상관없다.

시작점에서 도착점까지 도달하는 수많은 경로들이 있다 이 중에 꺾이는 코너가 최소한인 경로를 찾으면 될 것 같다.

꺾이는 코너를 어떻게 판단할까? → 이동 방향을 기록하고 비교를 통해 가능하다.

두 점 사이의 거울 개수를 최소로 하는 것이 최단거리와 관련 있을까? → 최단거리보다 긴 거리인데 거울의 개수가 적게 들어가는 경우가 존재하는지 생각해봐야 한다. 최단거리가 아니라는 것은 코너링이 더 추가된다는 뜻이기에 최단거리일 때 답 후보가 있다.

  1. 최단거리를 구해야 하기 때문에 bfs를 사용하겠다.
  2. 최단거리로 도착점에 도달했을 때의 코너 개수를 갱신한다.
  3. 같은 지점이라도 여러 방향에서 도달할 수 있고 도달하는데 들어간 코너 개수가 다를 수 있다.
package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Queue;
import java.util.StringTokenizer;

public class BOJ6087 {

	static class Point {
		int r;
		int c;
		int d;
		int count;

		public Point(int r, int c, int d, int count) {
			this.r = r;
			this.c = c;
			this.d = d;
			this.count = count;
		}

		boolean isGoal() {
			return this.r == goal[0] && this.c == goal[1];
		}
	}
	static final int[][] DIRECTION = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    static FastReader scan = new FastReader();
	static int W, H;
	static char[][] map;
	static int[] start;
	static int[] goal;
	static int[][] cPos;

    static void input() {
		W = scan.nextInt();
		H = scan.nextInt();
		map = new char[H][W];
		cPos = new int[2][2];
		int idx = 0;
		for (int i = 0; i < H; i++) {
			map[i] = scan.next().toCharArray();
			for (int j = 0; j < W; j++) {
				if (map[i][j] == 'C') {
					cPos[idx++] = new int[]{i, j};
				}
			}
		}
		start = cPos[0];
		goal = cPos[1];
    }

	static void bfs(int[] start) {
		Queue<Point> q = new ArrayDeque<>();
		int[][][] visited = new int[H][W][4];
		for (int i = 0; i < H; i++) {
			for (int j = 0; j < W; j++) {
				Arrays.fill(visited[i][j], Integer.MAX_VALUE);
			}
		}

		for (int d = 0; d < 4; d++) {
			q.add(new Point(start[0], start[1], d, 0));
			visited[start[0]][start[1]][0] = 0;
 		}

		int mirrorCount = Integer.MAX_VALUE;
		while (!q.isEmpty()) {
			Point curr = q.poll();
			if (curr.isGoal()) {
				mirrorCount = Math.min(mirrorCount, curr.count);
				continue;
			}

			for (int d = 0; d < 4; d++) {
				int nr = curr.r + DIRECTION[d][0];
				int nc = curr.c + DIRECTION[d][1];

				if (!inRange(nr, nc)) continue;
				if (isWall(nr, nc)) continue;

				int nextCount = curr.count;
				if (!isStart(curr.r, curr.c) && isConner(curr.d, d)) {
					nextCount++;
				}

				if (visited[nr][nc][d] > nextCount) {
					visited[nr][nc][d] = nextCount;
					q.add(new Point(nr, nc, d, nextCount));
				}
			}
		}

		System.out.println(mirrorCount);
	}

	static boolean isStart(int r, int c) {
		return start[0] == r && start[1] == c;
	}

	static boolean isConner(int d1, int d2) {
		if (d1 == 0) {
			return d2 != 0 && d2 != 1;
		} else if (d1 == 1) {
			return d2 != 0 && d2 != 1;
		} else if (d1 == 2) {
			return d2 != 2 && d2 != 3;
		} else {
			return d2 != 2 && d2 != 3;
		}
	}

	static boolean inRange(int r, int c) {
		return r >= 0 && r < H && c >= 0 && c < W;
	}

	static boolean isWall(int r, int c) {
		return map[r][c] == '*';
	}

    public static void main(String[] args) {
		input();
		bfs(start);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 4991 - 로봇 청소기  (0) 2024.11.11
BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 10021 - Watering the Fields  (0) 2024.11.06
BOJ 2292 - 벌집  (5) 2024.11.06
BOJ 2933 - 미네랄  (0) 2024.11.05

10021번: Watering the Fields

 

N개의 필드 존재 (1 ≤ N ≤ 2000)

각 필드는 2차원 평면상 (xi, yi)로 표시(0≤ xi, yi ≤ 1000)

$$ i,j 필드 사이 비용 = (x_i-x_j)^2 + (y_i-y_j)^2 $$

모든 필드를 최소비용으로 연결하고 싶다.

그런데 비용이 C 이상인 파이프만 설치할 수 있다. (1≤ C≤ 1,000,000)

크루스칼 알고리즘을 사용하면 될 것 같다.

  1. 주어진 정보로 간선을 계산해 저장한다.
  2. 비용이 작은 간선부터 꺼내 두 노드를 연결한다.
  3. 노드 연결 시 이미 같은 집합이거나 비용이 C보다 작다면 연결하지 않는다.
package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class BOJ10021 {

    static class Edge {
        int from;
        int to;
        int cost;

        public Edge(int from, int to, int cost) {
            this.from = from;
            this.to = to;
            this.cost = cost;
        }

        @Override
        public String toString() {
            return from + "," + to + "," + cost;
        }
    }

    static FastReader scan = new FastReader();
    static int N, C;
    static int[][] fields;
    static List<Edge>[] graph;

    static void input() {
        N = scan.nextInt();
        C = scan.nextInt();
        fields = new int[N][2];
        for (int i = 0; i < N; i++) {
            fields[i] = new int[]{scan.nextInt(), scan.nextInt()};
        }
    }

    static void initGraph() {
        graph = new ArrayList[N];
        for (int i = 0; i < N; i++) {
            graph[i] = new ArrayList<>();
        }

        // 각 노드부터 출발한 거리 계산
        for (int i = 0; i < N; i++) {
            int[] from = fields[i];
            for (int j = i + 1; j < N; j++) {
                int[] to = fields[j];
                int cost = calcCost(from, to);
                graph[i].add(new Edge(i, j, cost));
            }
        }
    }

    static int calcCost(int[] from, int[] to) {
        return (int) (Math.pow((from[0] - to[0]), 2) + Math.pow((from[1] - to[1]), 2));
    }

    static void solve() {
        int totalCost = 0;
        int edgeCount = 0;
        PriorityQueue<Edge> pq = new PriorityQueue<>(Comparator.comparingInt(e -> e.cost));
        for (int i = 0; i < N; i++) {
            for (Edge e : graph[i]) {
                if (e.cost < C) continue;
                pq.add(e);
            }
        }
        int[] parent = new int[N];
        for (int i = 0; i < N; i++) {
            parent[i] = i;
        }
        while (!pq.isEmpty()) {
            Edge curr = pq.poll();
            int from = curr.from;
            int to = curr.to;
            int cost = curr.cost;

            if (isConnected(parent, from, to)) {
                continue;
            }

            union(parent, from, to);
            totalCost += cost;
            edgeCount++;
        }

        System.out.println(edgeCount == N - 1 ? totalCost : -1);
    }

    static boolean isConnected(int[] parent, int x, int y) {
        return find(parent, x) == find(parent, y);
    }

    static void union(int[] parent, int x, int y) {
        int rootX = find(parent, x);
        int rootY = find(parent, y);

        if (rootX < rootY) {
            parent[rootY] = rootX;
        } else {
            parent[rootX] = rootY;
        }
    }

    static int find(int[] parent, int x) {
        if (parent[x] != x) {
            parent[x] = find(parent, parent[x]);
        }
        return parent[x];
    }

    public static void main(String[] args) {
        input();
        initGraph();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 11049 - 행렬 곱셈 순서  (1) 2024.11.09
BOJ 6087 - 레이저 통신  (0) 2024.11.07
BOJ 2292 - 벌집  (5) 2024.11.06
BOJ 2933 - 미네랄  (0) 2024.11.05
BOJ 15591 - MooTube (Silver)  (0) 2024.10.31

2292번: 벌집

 

중앙 1에서 N번 방까지 최소 개수의 방을 지나서 갈 때 몇 개의 방을 지나는가.

해당 수가 몇 레벨에 존재하는 지를 알아야 한다.

1 → 1 레벨

2 ~ 7 → 2 레벨

8 ~ 19 → 3 레벨

20 → 37 → 4 레벨 이런 식으로 증가하고 있다. N = 13 이면 3 레벨에 존재하기 때문에 답은 3이다. 그리고 N = 58이면 5 레벨에 있기 때문에 답은 5이다.

육각형으로 증가하는데 1 레벨은 길이가 1인 육각형, 2 레벨을 길이가 2인 육각형이다.

3 레벨은 3*6 - 6이다. 즉 해당 레벨에 존재하는 칸의 개수는 6 * (X - 1) 단, X = 1 이면 1이 된다.

2 계층은 6개 3 계층은 12개가 존재한다.

마지막 번호도 규칙이 있다.

1번 레벨 : 1

2번 레벨 : 7

3번 레벨 : 19

4번 레벨 : 37

이렇게 된다. 즉 이전 마지막 번호 + (6 * (X-1))이다.

이제 주어진 N이 어느 범위인지 판단하면 된다. 마지막 숫자보다 작으면 가능한 후보이며 마지막 숫자보다 큰 순간이면 바로 이전 레벨이 답이다.

package boj;

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

public class BOJ2292 {

    static FastReader scan = new FastReader();
    static int N;

    static void input() {
        N = scan.nextInt();
    }

    static void solve() {
        int level = 2;
        int end = 1;

        while (end < N) {
            end += 6 * (level - 1);
            level++;
        }

        System.out.println(level - 1);
    }

    public static void main(String[] args) {
        input();
        solve();
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 6087 - 레이저 통신  (0) 2024.11.07
BOJ 10021 - Watering the Fields  (0) 2024.11.06
BOJ 2933 - 미네랄  (0) 2024.11.05
BOJ 15591 - MooTube (Silver)  (0) 2024.10.31
BOJ 10830 - 행렬의 제곱  (0) 2024.10.31

2933번: 미네랄

 

동굴 내에서 막대 던지기를 한다. 막대는 미네랄을 파괴할 수도 있다.

R행 C열의 동굴. 각 칸은 빈칸 혹은 미네랄. 네 방향 중 하나로 인접한 미네랄 = 같은 클러스터

번갈아가며 막대를 던진다. 막대를 던지기 전에 던질 높이를 정한다. 막대는 땅과 수평을 이루며 날아간다.

날아가는 도중에 미네랄을 만나면 그 칸의 미네랄은 모두 파괴되며 막대는 그 자리에서 멈춘다.

클러스터라는 개념이 등장한다. 미네랄이 파괴된 이후 남은 클러스터가 분리될 수 있다고 하는데 이 부분부터 이해가 필요하다.

지도가 2차원 평면을 나타내는 것이 아니라 세로가 높이를 나타내고 있다.

막대를 던지는 높이는 1 ~ R 사이로 주어지며 이를 통해서 미네랄이 상하로 갈라지는 것이다.

필요한 기능

  1. 턴을 구분한다.→ height를 순회하면서 %2 == 0 이면 left → right이다.
  2. 막대를 던지는 기능 → height가 r이 돼서 거기부터 c를 증가시켜 가며 처음 미네랄을 만나는 곳을 찾는다.
  3. 미네랄 클러스터 판단 클러스터를 확인할 수 있어야 한다. 클러스터가 하강하는 경우 = 바닥과 연결이 안 돼 있다.
    1. 미네랄과 충돌할 시 해당 위치의 4방향 기준 시작점으로 추가한 뒤 탐색을 시작
    2. 탐색 중 바닥과 연결되지 않은 것이 하강하는 클러스터
  4. 하강할 클러스터를 어떻게 하강시키는가? 떨어지는 동안 클러스터의 모양이 유지된다.
    1. 떨어지다가 바닥에 도달하지 않았는데 다른 미네랄에 걸리는 경우
    2. 걸리지 않고 가장 아래 부분이 바닥에 도달하는 경우
package boj;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

public class BOJ2933 {

    static final int[][] DIRECTION = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; //상하좌우
    static FastReader scan = new FastReader();
    static int R, C, N;
    static char[][] initialMap;
    static int[] heights;

    static class Result {
        boolean isDown;
        List<int[]> cand;

        public Result(boolean isDown, List<int[]> cand) {
            this.isDown = isDown;
            this.cand = cand;
        }
    }

    static void input() {
        R = scan.nextInt();
        C = scan.nextInt();
        initialMap = new char[R][C];
        for (int i = 0; i < R; i++) {
            initialMap[i] = scan.next().toCharArray();
        }
        N = scan.nextInt();
        heights = new int[N];
        for (int i = 0; i < N; i++) {
            heights[i] = R - scan.nextInt(); // 높이
        }
    }

    static void print(char[][] map) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < map.length; i++) {
            sb.append(map[i]);
            if (i != map.length -1) {
                sb.append("\\n");
            }
        }
        System.out.println(sb);
    }

    static void solve(char[][] map) {
        for (int i = 0; i < N; i++) {
            int height = heights[i];
            int[] target;
            if (i % 2 == 0) {
                target = findMineral(map, height, 0);
            } else {
                // right to left
                target = findMineral(map, height, 1);
            }
            if (target == null) {
                continue; // 맞은 미네랄이 없다.
            }

            map[target[0]][target[1]] = '.';

            // 해당 타겟 기중 상하좌우를 보고 하강 클러스터 후보 찾기
            List<int[]> cand = new ArrayList<>();
            for (int d = 0; d < 4; d++) {
                int nr = target[0] + DIRECTION[d][0];
                int nc = target[1] + DIRECTION[d][1];
                if (!inRange(nr, nc)) {
                    continue;
                }
                if (map[nr][nc] == 'x') {
                    cand.add(new int[]{nr, nc});
                }
            }

            if (cand.isEmpty()) {
                // 상하좌우에 연결된 미네랄이 없다 -> 클러스터 후보가 없다.
                continue;
            }

            List<List<int[]>> clusters = new ArrayList<>();
            boolean[][] visited = new boolean[R][C];

            for (int[] mineral : cand) {
                if (!visited[mineral[0]][mineral[1]]) {
                    Result result = findDownCluster(map, mineral);
                    if (result.isDown) {
                        clusters.add(result.cand);
                    }
                    for (int[] pos : result.cand) {
                        visited[pos[0]][pos[1]] = true;
                    }
                }
            }

            for (List<int[]> cluster : clusters) {
                for (int[] pos : cluster) {
                    map[pos[0]][pos[1]] = '.';
                }

                int minHeight = calcDownHeight(cluster, map);

                for (int[] pos : cluster) {
                    map[pos[0] + minHeight][pos[1]] = 'x';
                }
            }
        }
        print(map);
    }

    static int calcDownHeight(List<int[]> cluster, char[][] map) {
        int minHeight = Integer.MAX_VALUE;
        for (int[] pos : cluster) {
            int r = pos[0];
            int c = pos[1];
            int h = 0;
            r++;
            while (inRange(r, c) && map[r][c] != 'x') {
                h++;
                r++;
            }
            minHeight = Math.min(minHeight, h);
        }
        return minHeight;
    }

    static Result findDownCluster(char[][] map, int[] start) {
        Queue<int[]> q = new ArrayDeque<>();
        boolean[][] visited = new boolean[R][C];
        q.add(start);
        visited[start[0]][start[1]] = true;
        List<int[]> cand = new ArrayList<>();
        boolean isBottom = false;

        while (!q.isEmpty()) {
            int[] curr = q.poll();
            cand.add(curr);
            int r = curr[0];
            int c = curr[1];
            if (r == R - 1) {
                // 밑바닥 도달
                isBottom = true;
            }

            for (int d = 0; d < 4; d++) {
                int nr = r + DIRECTION[d][0];
                int nc = c + DIRECTION[d][1];

                if (!inRange(nr, nc)) continue;
                if (visited[nr][nc]) continue;
                if (map[nr][nc] == 'x') {
                    q.add(new int[]{nr, nc});
                    visited[nr][nc] = true;
                }
            }
        }
        return isBottom ? new Result(false, List.of()) : new Result(true, cand);
    }

    static boolean inRange(int r, int c) {
        return r >= 0 && r < R && c >= 0 && c < C;
    }

    static int[] findMineral(char[][] map, int r, int dir) {
        if (dir == 0) {
            // left to right
            for (int i = 0; i < C; i++) {
                if (map[r][i] == 'x') {
                    return new int[]{r, i};
                }
            }
        } else {
            for (int i = C - 1; i >= 0; i--) {
                if (map[r][i] == 'x') {
                    return new int[]{r, i};
                }
            }
        }
        return null;
    }

    public static void main(String[] args) {
        input();
        solve(copy(initialMap));
    }

    private static char[][] copy(char[][] initialMap) {
        return Arrays.stream(initialMap)
                .map(char[]::clone)
                .toArray(char[][]::new);
    }

    static class FastReader {
        BufferedReader br;
        StringTokenizer st;

        public FastReader() {
            br = new BufferedReader(new InputStreamReader(System.in));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return st.nextToken();
        }

        int nextInt() {
            return Integer.parseInt(next());
        }
    }
}

'Algorithm > BOJ' 카테고리의 다른 글

BOJ 10021 - Watering the Fields  (0) 2024.11.06
BOJ 2292 - 벌집  (5) 2024.11.06
BOJ 15591 - MooTube (Silver)  (0) 2024.10.31
BOJ 10830 - 행렬의 제곱  (0) 2024.10.31
BOJ 12865 - 평범한 배낭  (0) 2024.10.31

+ Recent posts