www.acmicpc.net/problem/1967

 

1967번: 트리의 지름

파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연

www.acmicpc.net

문제

트리(tree)는 사이클이 없는 무방향 그래프이다. 트리에서는 어떤 두 노드를 선택해도 둘 사이에 경로가 항상 하나만 존재하게 된다. 트리에서 어떤 두 노드를 선택해서 양쪽으로 쫙 당길 때, 가장 길게 늘어나는 경우가 있을 것이다. 이럴 때 트리의 모든 노드들은 이 두 노드를 지름의 끝 점으로 하는 원 안에 들어가게 된다.

이런 두 노드 사이의 경로의 길이를 트리의 지름이라고 한다. 정확히 정의하자면 트리에 존재하는 모든 경로들 중에서 가장 긴 것의 길이를 말한다.

입력으로 루트가 있는 트리를 가중치가 있는 간선들로 줄 때, 트리의 지름을 구해서 출력하는 프로그램을 작성하시오. 아래와 같은 트리가 주어진다면 트리의 지름은 45가 된다.

트리의 노드는 1부터 n까지 번호가 매겨져 있다.

입력

파일의 첫 번째 줄은 노드의 개수 n(1 ≤ n ≤ 10,000)이다. 둘째 줄부터 n-1개의 줄에 각 간선에 대한 정보가 들어온다. 간선에 대한 정보는 세 개의 정수로 이루어져 있다. 첫 번째 정수는 간선이 연결하는 두 노드 중 부모 노드의 번호를 나타내고, 두 번째 정수는 자식 노드를, 세 번째 정수는 간선의 가중치를 나타낸다. 간선에 대한 정보는 부모 노드의 번호가 작은 것이 먼저 입력되고, 부모 노드의 번호가 같으면 자식 노드의 번호가 작은 것이 먼저 입력된다. 루트 노드의 번호는 항상 1이라고 가정하며, 간선의 가중치는 100보다 크지 않은 양의 정수이다.

출력

첫째 줄에 트리의 지름을 출력한다.

예제 입력 1

12

1

2

3

1

3

2

2

4

5

3

5

11

3

6

9

4

7

1

4

8

7

5

9

15

5

10

4

6

11

6

6

12

10

예제 출력 1

45

 

 

 

 

 

 

풀이 .

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

class Pair {
    int node;
    int dist;
    public Pair(int node, int dist) {
        this.node = node;
        this.dist = dist;
    }
}

public class Main {

    static ArrayList<Pair>[] map = null;
    static boolean[] check = null;
    static int[] dist = null;

    public static void bfs(int start) {
        Queue<Integer> que = new ArrayDeque<>();
        check[start] = true;
        que.add(start);

        while(!que.isEmpty()) {
            int now = que.poll();
            for(Pair p : map[now]) {
                int next = p.node;
                int len = p.dist;
                if(!check[next]) {
                    check[next] = true;
                    dist[next] = dist[now] + len;
                    que.add(next);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());

        map = new ArrayList[n + 1];
        check = new boolean[n + 1];
        dist = new int[n + 1];
        for(int i = 1; i <= n; i++) {
            map[i] = new ArrayList<>();
        }

        for(int i = 0; i < n-1; i++) {
            st = new StringTokenizer(br.readLine());
            int parent = Integer.parseInt(st.nextToken());
            int child = Integer.parseInt(st.nextToken());
            int dist = Integer.parseInt(st.nextToken());
            map[parent].add(new Pair(child, dist));
            map[child].add(new Pair(parent, dist));
        }

        bfs(1);  // 임의의 점에서 가장 먼 노드를 찾는다
        int node = 1;
        for(int i = 2; i <= n; i++) {
            if(dist[node] < dist[i]) {
                node = i;
            }
        }

        // 초기화 후 BFS 다시 진행
        check = new boolean[n + 1];
        dist = new int[n + 1];  // 임의의 점에서 가장 먼 노드를 시작점으로 한 길이 배열
        bfs(node);  // 위에서 찾은 노드에서 가장 먼 노드를 찾는다
        int diameter = Arrays.stream(dist).max().getAsInt();
        System.out.println(diameter);
    }
}

 

codeung.tistory.com/186 와 문제의 이름부터 알고리즘까지 완전히 같은 문제.

 

입력만 바꿔서 똑같은 방법으로 풀어주면 된다.

www.acmicpc.net/problem/1167

 

1167번: 트리의 지름

트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2≤V≤100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. (정점 번호는 1부터 V까지

www.acmicpc.net

문제

트리의 지름이란, 트리에서 임의의 두 점 사이의 거리 중 가장 긴 것을 말한다. 트리의 지름을 구하는 프로그램을 작성하시오.

입력

트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2≤V≤100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. (정점 번호는 1부터 V까지 매겨져 있다고 생각한다)

먼저 정점 번호가 주어지고, 이어서 연결된 간선의 정보를 의미하는 정수가 두 개씩 주어지는데, 하나는 정점번호, 다른 하나는 그 정점까지의 거리이다. 예를 들어 네 번째 줄의 경우 정점 3은 정점 1과 거리가 2인 간선으로 연결되어 있고, 정점 4와는 거리가 3인 간선으로 연결되어 있는 것을 보여준다. 각 줄의 마지막에는 -1이 입력으로 주어진다. 주어지는 거리는 모두 10,000 이하의 자연수이다.

출력

첫째 줄에 트리의 지름을 출력한다.

예제 입력 1

5

1 3 2 -1

2 4 4 -1

3 1 2 4 3 -1

4 2 4 3 3 5 6 -1

5 4 6 -1

예제 출력 1

11

 

 

 

 

 

 

풀이 .

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

class Pair {
    int node;
    int dist;
    public Pair(int node, int dist) {
        this.node = node;
        this.dist = dist;
    }
}

public class Main {

    static ArrayList<Pair>[] map = null;
    static boolean[] check = null;
    static int[] dist = null;

    public static void bfs(int start) {
        Queue<Integer> que = new ArrayDeque<>();
        check[start] = true;
        que.add(start);

        while(!que.isEmpty()) {
            int now = que.poll();
            for(Pair p : map[now]) {
                int next = p.node;
                int len = p.dist;
                if(!check[next]) {
                    check[next] = true;
                    dist[next] = dist[now] + len;
                    que.add(next);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());

        map = new ArrayList[n + 1];
        check = new boolean[n + 1];
        dist = new int[n + 1];
        for(int i = 1; i <= n; i++) {
            map[i] = new ArrayList<>();
        }

        for(int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            int node = Integer.parseInt(st.nextToken());
            while(st.hasMoreTokens()) {
                int connectedNode = Integer.parseInt(st.nextToken());
                if(connectedNode == -1) break;
                int dist = Integer.parseInt(st.nextToken());
                map[node].add(new Pair(connectedNode, dist));
            }
        }

        bfs(1);  // 임의의 점에서 가장 먼 노드를 찾는다
        int node = 1;
        for(int i = 2; i <= n; i++) {
            if(dist[node] < dist[i]) {
                node = i;
            }
        }

        // 초기화 후 BFS 다시 진행
        check = new boolean[n + 1];
        dist = new int[n + 1];  // 임의의 점에서 가장 먼 노드를 시작점으로 한 길이 배열
        bfs(node);  // 위에서 찾은 노드에서 가장 먼 노드를 찾는다
        int diameter = Arrays.stream(dist).max().getAsInt();
        System.out.println(diameter);
    }
}

 

트리의 지름 구하는 방법

1. 임의의 노드(보통 루트로 함)에서 가장 먼 노드를 구한다.

2. 1에서 찾은 가장 먼 노드에서 다시 가장 먼 노드까지의 거리가 트리의 지름이 된다.

 

즉, 탐색을 두 번 진행해야 한다.

 

주의할 점,

1. 두 번째 탐색을 진행하기 전에 boolean[] check, int[] dist 등에 대한 초기화를 진행해야 한다는 것이다.

2. 에지들의 길이가 모두 다르기 때문에 Pair을 사용해서 에지의 거리까지 함께 기억한다.

 

 

 

www.acmicpc.net/problem/11725

 

11725번: 트리의 부모 찾기

루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.

www.acmicpc.net

문제

루트 없는 트리가 주어진다. 이때, 트리의 루트를 1이라고 정했을 때, 각 노드의 부모를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 노드의 개수 N (2 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N-1개의 줄에 트리 상에서 연결된 두 정점이 주어진다.

출력

첫째 줄부터 N-1개의 줄에 각 노드의 부모 노드 번호를 2번 노드부터 순서대로 출력한다.

예제 입력 1

7

1 6

6 3

3 5

4 1

2 4

4 7

예제 출력 1

4

6

1

3

1

4

예제 입력 2

12

1 2

1 3

2 4

3 5

3 6

4 7

4 8

5 9

5 1

0 6

11

6 12

예제 출력 2

1

1

2

3

3

4

4

5

5

6

6

 

 

 

 

 

 

풀이 .

import java.io.*;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {

    static ArrayList<Integer>[] map = null;
    static int[] parent = null;
    static boolean[] check = null;

    public static void dfs(int node) {
        for(int next : map[node]) {
            if(!check[next]) {
                check[next] = true;
                parent[next] = node;
                dfs(next);
            }
        }
    }

    public static void bfs(int root) {
        Queue<Integer> que = new ArrayDeque<>();
        que.add(root);
        check[root] = true;

        while(!que.isEmpty()) {
            int now = que.poll();
            for(int next : map[now]) {
                if(!check[next]) {
                    check[next] = true;
                    parent[next] = now;
                    que.add(next);
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = null;

        int n = Integer.parseInt(br.readLine());
        map = new ArrayList[n + 1];
        parent = new int[n + 1];
        check = new boolean[n + 1];
        for(int i = 1; i <= n; i++) {
            map[i] = new ArrayList<>();
        }

        for(int i = 0; i < n-1; i++) {
            st = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
            map[u].add(v);
            map[v].add(u);
        }

//        dfs(1); check[1] = true;
        bfs(1);
        for(int i = 2; i <= n; i++) {
            sb.append(parent[i] + "\n");
        }
        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

DFS, BFS 를 사용해 트리의 탐색을 할 수 있다.

(트리도 그래프이다. 사이클이 없으며 반드시 모든 노드가 연결된 그래프.)

 

int[] parent를 사용해 탐색을 진행하며 각 노드의 부모노드를 알아낼 수 있다.

(루트를 제외한 모든 노드는 하나의 부모만을 갖기 때문에 next로 넘어가기 전 노드인 now가 next의 노드가 된다.)

www.acmicpc.net/problem/1991

 

1991번: 트리 순회

첫째 줄에는 이진 트리의 노드의 개수 N(1≤N≤26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 영문자

www.acmicpc.net

 

문제

이진 트리를 입력받아 전위 순회(preorder traversal), 중위 순회(inorder traversal), 후위 순회(postorder traversal)한 결과를 출력하는 프로그램을 작성하시오.

예를 들어 위와 같은 이진 트리가 입력되면,

  • 전위 순회한 결과 : ABDCEFG // (루트) (왼쪽 자식) (오른쪽 자식)
  • 중위 순회한 결과 : DBAECFG // (왼쪽 자식) (루트) (오른쪽 자식)
  • 후위 순회한 결과 : DBEGFCA // (왼쪽 자식) (오른쪽 자식) (루트)

가 된다.

입력

첫째 줄에는 이진 트리의 노드의 개수 N(1≤N≤26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식 노드, 오른쪽 자식 노드가 주어진다. 노드의 이름은 A부터 차례대로 영문자 대문자로 매겨지며, 항상 A가 루트 노드가 된다. 자식 노드가 없는 경우에는 .으로 표현된다.

출력

첫째 줄에 전위 순회, 둘째 줄에 중위 순회, 셋째 줄에 후위 순회한 결과를 출력한다. 각 줄에 N개의 알파벳을 공백 없이 출력하면 된다.

예제 입력 1

7

A B C

B D .

C E F

E . .

F . G

D . .

G . .

예제 출력 1

ABDCEFG

DBAECFG

DBEGFCA

 

 

 

 

 

 

 

풀이 .

import java.io.*;
import java.util.StringTokenizer;

public class Main {
    static BufferedReader br = null;
    static BufferedWriter bw = null;
    static StringTokenizer st = null;
    static StringBuilder sb = null;
    static char[][] tree = null;

    public static void preorder(int node) {
        sb.append(tree[node][0]);
        if(tree[node][1] != '.') {
            preorder(tree[node][1] - 65);
        }
        if(tree[node][2] != '.') {
            preorder(tree[node][2] - 65);
        }
    }

    public static void inorder(int node) {
        if(tree[node][1] != '.') {
            inorder(tree[node][1] - 65);
        }
        sb.append(tree[node][0]);
        if(tree[node][2] != '.') {
            inorder(tree[node][2] - 65);
        }
    }

    public static void postorder(int node) {
        if(tree[node][1] != '.') {
            postorder(tree[node][1] - 65);
        }
        if(tree[node][2] != '.') {
            postorder(tree[node][2] - 65);
        }
        sb.append(tree[node][0]);
    }

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

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

        tree = new char[n][3];
        for(int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            char node = st.nextToken().charAt(0);  // 노드의 문자를 인덱스로 맞춰준다.
            tree[node - 65][0] = node;
            tree[node - 65][1] = st.nextToken().charAt(0);
            tree[node - 65][2] = st.nextToken().charAt(0);
        }

        preorder(0); sb.append("\n");
        inorder(0); sb.append("\n");
        postorder(0); sb.append("\n");

        bw.write(sb.toString());
        br.close();
        bw.flush();
        bw.close();
    }
}

 

tree[node][0] = node

tree[node][1] = node의 왼쪽 자식

tree[node][2] = node의 오른쪽 자식

 

각 순회의 조건에 맞추어 재귀를 사용해 처리한다.

 

입력이 A, B, C 순이 아닌 무작위란 것에 주의해야 한다.

 

알파벳 순 입력으로 착각하여 반복변수 i를 행번호로 두었다가 틀렸었다.

알파벳이 각자 자신의 인덱스를 찾아가도록 직접 지정해주어야 한다.

www.acmicpc.net/problem/2146

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다

www.acmicpc.net

문제

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은 섬을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다는 생각을 하게 되었다. 그래서 그는, 생색내는 식으로 한 섬과 다른 섬을 잇는 다리 하나만을 만들기로 하였고, 그 또한 다리를 가장 짧게 하여 돈을 아끼려 하였다.

이 나라는 N×N크기의 이차원 평면상에 존재한다. 이 나라는 여러 섬으로 이루어져 있으며, 섬이란 동서남북으로 육지가 붙어있는 덩어리를 말한다. 다음은 세 개의 섬으로 이루어진 나라의 지도이다.

위의 그림에서 색이 있는 부분이 육지이고, 색이 없는 부분이 바다이다. 이 바다에 가장 짧은 다리를 놓아 두 대륙을 연결하고자 한다. 가장 짧은 다리란, 다리가 격자에서 차지하는 칸의 수가 가장 작은 다리를 말한다. 다음 그림에서 두 대륙을 연결하는 다리를 볼 수 있다.

물론 위의 방법 외에도 다리를 놓는 방법이 여러 가지 있으나, 위의 경우가 놓는 다리의 길이가 3으로 가장 짧다(물론 길이가 3인 다른 다리를 놓을 수 있는 방법도 몇 가지 있다).

지도가 주어질 때, 가장 짧은 다리 하나를 놓아 두 대륙을 연결하는 방법을 찾으시오.

입력

첫 줄에는 지도의 크기 N(100이하의 자연수)가 주어진다. 그 다음 N줄에는 N개의 숫자가 빈칸을 사이에 두고 주어지며, 0은 바다, 1은 육지를 나타낸다. 항상 두 개 이상의 섬이 있는 데이터만 입력으로 주어진다.

출력

첫째 줄에 가장 짧은 다리의 길이를 출력한다.

예제 입력 1

10

1 1 1 0 0 0 0 1 1 1

1 1 1 1 0 0 0 0 1 1

1 0 1 1 0 0 0 0 1 1

0 0 1 1 1 0 0 0 0 1

0 0 0 1 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 0

0 0 0 0 1 1 0 0 0 0

0 0 0 0 1 1 1 0 0 0

0 0 0 0 0 0 0 0 0 0

예제 출력 1

3

 

 

 

 

 

 

풀이 .

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

class Pair {
    int r;
    int c;
    public Pair(int r, int c) {
        this.r = r;
        this.c = c;
    }
}

public class Main {
    static int[][] map = null;
    static int[][] component = null;
    static boolean[][] check = null;

    static int[] rArr = {-1, 1, 0, 0};
    static int[] cArr = {0, 0, -1, 1};

    static int n;

    public static void dfs(int r, int c, int comp) {
        for(int i = 0; i < 4; i++) {
            int nr = r + rArr[i];
            int nc = c + cArr[i];
            if(0 <= nr && nr < n && 0 <= nc && nc < n &&
                    map[nr][nc] == 1 && !check[nr][nc]) {
                check[nr][nc] = true;
                component[nr][nc] = comp;
                dfs(nr, nc, comp);
            }
        }
    }

    public static int bfs() {
        int ans = n * n;  // 다리의 길이는 n^2을 넘을 수 없다
        Queue<Pair> que = new ArrayDeque<>();
        for(int i = 0; i < n; i++) {  // 일단 모든 섬들을 큐에 담는다. 한 섬당 한 칸 씩 차례대로 다리를 올릴 것이다
            for(int j = 0; j < n; j++) {
                if(map[i][j] == 1) {
                    que.add(new Pair(i, j));
                }
            }
        }

        while(!que.isEmpty()) {
            Pair p = que.poll();
            for(int i = 0; i < 4; i++) {
                int nr = p.r + rArr[i];
                int nc = p.c + cArr[i];
                if(0 <= nr && nr < n && 0 <= nc && nc < n) {
                    if(map[nr][nc] == 0) {
                        map[nr][nc] = map[p.r][p.c] + 1;  // 다리를 한 칸 올린다
                        component[nr][nc] = component[p.r][p.c];  // 새로 올라간 다리도 섬의 일부로 친다
                        que.add(new Pair(nr, nc));
                    }else {
                        if(component[nr][nc] == component[p.r][p.c]) {  // 같은 섬은 패스
                            continue;
                        }else {  // 다른 섬 만남
                            // 다른 섬을 만났을 때 (우리 섬에서 올린 다리길이 + 다른 섬에서 올린 다리길이)가 합쳐진 다리의 길이이다
                            // 두 섬을 잇는 다리가 완성되었으니 더이상 큐에 추가하지 않는다
                            int bridge = (map[p.r][p.c] - 1) + (map[nr][nc] - 1); // 섬의 숫자 1까지 길이에 포함되었으니 -1
                            ans = Math.min(ans, bridge);
                        }
                    }
                }
            }
        }
        return ans;
    }

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

        map = new int[n][n];
        component = new int[n][n];
        check = new boolean[n][n];
        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());
            }
        }

        int comp = 1;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(map[i][j] == 1 && !check[i][j]) {
                    check[i][j] = true;
                    component[i][j] = comp;
                    dfs(i, j, comp);
                    comp += 1;
                }
            }
        }
        int ans = bfs();
        System.out.println(ans);
    }
}

 

과거에 C++로 PS를 하던 시절에 탈탈 털리고 포기했던 문제를 이번에 자바로 다시 풀었다.

처음으로 혼자 힘으로 골드3 문제를 풀어서 매우 기분이 좋다.

 

구해야 하는 것은 "가장 짧은 다리의 길이", BFS를 사용한 최단경로 문제라는 것을 알 수 있다.

 

 

int[][] map : 섬과 바다를 표시하는 지도. (이전 다리의 길이 + 1) 의 숫자를 0으로 갱신하여 바다에 다리를 올리는 것을 표현.

int[][] component : 각 섬을 구분하기 위해 사용한다. A섬에서부터 이어지기 시작한 다리 역시 A섬의 일부로 보아 A섬과 똑같은 숫자가 주어진다. 

boolean[][] check : int[][] component를 구하기 위한 DFS에서만 사용하는 체크용 배열. 이후 BFS에서는 map[i][j]==0으로 !check[i][j]를 대체한다.

 

 

핵심 아이디어는 모든 섬에서 순서대로 한 칸 씩 다리를 올려나가게 하는 것이다.

 

그렇게 한 칸 씩 차례대로 다리를 올리다 보면 다음에 다리를 올려야 하는 칸에 이미 다른 섬에서 올리고있는 다리가 존재하는 순간이 오게 된다.

 

그때 (나의 섬이 올리고있던 다리의 길이 + 다른 섬이 올리고있던 내가 만난 다리의 길이) 를 구하면 두 섬을 잇는 다리의 길이가 된다.

(이런식으로 다리의 최소값을 구하면 그게 정답이 된다.)

 

 

다른 섬의 다리를 만났다면 더이상 큐에 추가하지 않아도 된다.

www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

문제

N×M크기의 배열로 표현되는 미로가 있다.

1 0 1 1 1 1
1 0 1 0 1 0
1 0 1 0 1 1
1 1 1 0 1 1

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

입력

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

출력

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.

예제 입력 1

4 6

101111

101010

101011

111011

예제 출력 1

15

예제 입력 2

4 6

110110

110110

111111

111101

예제 출력 2

9

예제 입력 3

2 25

1011101110111011101110111

1110111011101110111011101

예제 출력 3

38

예제 입력 4

7 7

1011111

1110001

1000001

1000001

1000001

1000001

1111111

예제 출력 4

13

 

 

 

 

 

 

풀이 .

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

class Pair {
    int r;
    int c;
    public Pair(int r, int c) {
        this.r = r;
        this.c = c;
    }
}

public class Main {
    static int[][] map = null;
    static boolean[][] check = null;

    static int[] rArr = {-1, 1, 0, 0};
    static int[] cArr = {0, 0, -1, 1};

    static int n, m;

    public static void bfs(int r, int c) {
        Queue<Pair> que = new ArrayDeque<>();
        check[r][c] = true;
        que.add(new Pair(r, c));

        while(!que.isEmpty()) {
            Pair p = que.poll();
            for(int i = 0; i < 4; i++) {
                int nr = p.r + rArr[i];
                int nc = p.c + cArr[i];
                if(0 <= nr && nr < n && 0 <= nc && nc < m &&
                        !check[nr][nc] && map[nr][nc] != 0) {
                    check[nr][nc] = true;
                    map[nr][nc] = map[p.r][p.c] + 1;
                    que.add(new Pair(nr, nc));
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] str = br.readLine().split(" ");
        n = Integer.parseInt(str[0]);
        m = Integer.parseInt(str[1]);

        map = new int[n][m];
        check = new boolean[n][m];
        for(int i = 0; i < n; i++) {
            char[] ch = br.readLine().toCharArray();
            for(int j = 0; j < m; j++) {
                map[i][j] = ch[j] - '0';
            }
        }
        bfs(0, 0);
        System.out.println(map[n-1][m-1]);
    }
}

 

BFS를 사용한 최단경로 문제.

 

int[][] dist 를 따로 사용해도 되지만 그냥 int[][] map으로 전부 처리했다.

www.acmicpc.net/problem/7576

 

7576번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토

www.acmicpc.net

문제

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다. 

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.

예제 입력 1

6 4

0 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 1

예제 출력 1

8

예제 입력 2

6 4

0 -1 0 0 0 0

-1 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 1

예제 출력 2

-1

예제 입력 3

6 4

1 -1 0 0 0 0

0 -1 0 0 0 0

0 0 0 0 -1 0

0 0 0 0 -1 1

예제 출력 3

6

예제 입력 4

5 5

-1 1 0 0 0

0 -1 -1 -1 0

0 -1 -1 -1 0

0 -1 -1 -1 0

0 0 0 0 0

예제 출력 4

14

예제 입력 5

2 2

1 -1

-1 1

예제 출력 5

0

 

 

 

 

 

 

풀이 .

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

class Pair {
    int r;
    int c;
    public Pair(int r, int c) {
        this.r = r;
        this.c = c;
    }
}

public class Main {

    static int[][] map = null;
    static int[] rArr = {-1, 1, 0, 0};
    static int[] cArr = {0, 0, -1, 1};

    static int n, m;

    public static void bfs() {
        Queue<Pair> que = new ArrayDeque<>();
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(map[i][j] == 1) {
                    que.add(new Pair(i, j));
                }
            }
        }

        while(!que.isEmpty()) {
            Pair p  = que.poll();
            for(int i = 0; i < 4; i++) {
                int nr = p.r + rArr[i];
                int nc = p.c + cArr[i];
                if(0 <= nr && nr < n && 0 <= nc && nc < m &&
                        map[nr][nc] == 0) {
                    map[nr][nc] = map[p.r][p.c] + 1;
                    que.add(new Pair(nr, nc));
                }
            }
        }
    }

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

        m = Integer.parseInt(st.nextToken());  // 열
        n = Integer.parseInt(st.nextToken());  // 행
        map = new int[n][m];
        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());
            }
        }

        bfs();
        int max = 0;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(map[i][j] == 0) {
                    System.out.println(-1);
                    return;
                }
                if(max < map[i][j]) {
                    max = map[i][j];
                }
            }
        }
        System.out.println(max - 1);
    }
}

 

BFS를 사용해 최단경로를 구하는 문제.

 

잘 익은 토마토가 있는 모든 점을 시작점으로 하여 BFS를 실시한다.

 

BFS가 종료되었을 때 가장 멀리까지 간 곳이 마지막 날에 익는 토마토의 자리.

 

1부터 시작했기 때문에 -1 해주면 정답이 된다.

www.acmicpc.net/problem/4963

 

4963번: 섬의 개수

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다. 둘째 줄부터 h개 줄에는 지도

www.acmicpc.net

문제

정사각형으로 이루어져 있는 섬과 바다 지도가 주어진다. 섬의 개수를 세는 프로그램을 작성하시오.

한 정사각형과 가로, 세로 또는 대각선으로 연결되어 있는 사각형은 걸어갈 수 있는 사각형이다. 

두 정사각형이 같은 섬에 있으려면, 한 정사각형에서 다른 정사각형으로 걸어서 갈 수 있는 경로가 있어야 한다. 지도는 바다로 둘러싸여 있으며, 지도 밖으로 나갈 수 없다.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다.

둘째 줄부터 h개 줄에는 지도가 주어진다. 1은 땅, 0은 바다이다.

입력의 마지막 줄에는 0이 두 개 주어진다.

출력

각 테스트 케이스에 대해서, 섬의 개수를 출력한다.

예제 입력 1

1 1

0

2 2

0 1

1 0

3 2

1 1 1

1 1 1

5 4

1 0 1 0 0

1 0 0 0 0

1 0 1 0 1

1 0 0 1 0

5 4

1 1 1 0 1

1 0 1 0 1

1 0 1 0 1

1 0 1 1 1

5 5

1 0 1 0 1

0 0 0 0 0

1 0 1 0 1

0 0 0 0 0

1 0 1 0 1

0 0

예제 출력 1

0

1

1

3

1

9

 

 

 

 

 

 

풀이 .

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

public class Main {
    static int[][] map = null;
    static boolean[][] check = null;

    static int[] rArr = {-1, 1, 0, 0, -1, -1, 1, 1};
    static int[] cArr = {0, 0, -1, 1, 1, -1, -1, 1};

    static int w, h;

    public static void dfs(int r, int c) {
        for(int i = 0; i < 8; i++) {
            int nr = r + rArr[i];
            int nc = c + cArr[i];
            if(0 <= nr && nr < h && 0 <= nc && nc < w &&
                    !check[nr][nc] && map[nr][nc] == 1) {
                check[nr][nc] = true;
                dfs(nr, nc);
            }
        }
    }

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

        while(true) {
            st = new StringTokenizer(br.readLine());
            w = Integer.parseInt(st.nextToken());
            h = Integer.parseInt(st.nextToken());

            if(w == 0 && h == 0) break;

            map = new int[h][w];
            check = new boolean[h][w];
            for(int i = 0; i < h; i++) {
                st = new StringTokenizer(br.readLine());
                for(int j = 0; j < w; j++) {
                    map[i][j] = Integer.parseInt(st.nextToken());
                }
            }

            int cnt = 0;
            for(int i = 0; i < h; i++) {
                for(int j = 0; j < w; j++) {
                    if(!check[i][j] && map[i][j] == 1) {
                        check[i][j] = true;
                        dfs(i, j);
                        cnt += 1;
                    }
                }
            }
            System.out.println(cnt);
        }
    }
}

 

그냥 DFS 돌려서 구성요소의 개수를 구하면 된다.

 

주의할 점,

1. w,h는 행,열이 아니라 열,행이다.

2. 대각선 위치로도 이동이 가능하다.

+ Recent posts