문제

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

2 초 128 MB 1463 691 581 48.136%

문제

동혁이는 크로스워드 퍼즐을 좋아한다. R×C 크기의 크로스워드 퍼즐을 생각해 보자. 이 퍼즐은 R×C 크기의 표로 이루어지는데, 퍼즐을 다 풀면 금지된 칸을 제외하고는 각 칸에 알파벳이 하나씩 적혀 있게 된다. 아래는 R = 5, C = 5 인 경우 다 푼 퍼즐의 한 예이다. 검은 칸은 금지된 칸이다.

세로 또는 가로로 연속되어 있고, 더 이상 확장될 수 없는 낱말이 퍼즐 내에 존재하는 단어가 된다. 위의 퍼즐과 같은 경우, 가로 낱말은 good, an, messy, it, late의 5개가 있고, 세로 낱말은 game, one, sit, byte의 4개가 있다. 이 중 사전식 순으로 가장 앞서 있는 낱말은 an이다.

다 푼 퍼즐이 주어졌을 때, 퍼즐 내에 존재하는 모든 낱말 중 사전식 순으로 가장 앞서 있는 낱말을 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 퍼즐의 R과 C가 빈 칸을 사이에 두고 주어진다. (2 ≤ R, C ≤ 20) 이어서 R개의 줄에 걸쳐 다 푼 퍼즐이 주어진다. 각 줄은 C개의 알파벳 소문자 또는 금지된 칸을 나타내는 #로 이루어진다. 낱말이 하나 이상 있는 입력만 주어진다.

출력

첫째 줄에 사전식 순으로 가장 앞서 있는 낱말을 출력한다.

풀이

시간복잡도

퍼즐의 크기가 20X20 이하이고, 시간제한이 2초 이므로 시간에 대해서 크게 생각하지 않고 바로구현

  1. #을 만나기전까지는 해당 위치의 문자를 버퍼에 쌓는다.
  2. #을 만난경우 리스트에 문자열을 추가하고 버퍼를 비운다.
  3. #을 만났지만 버퍼 문자열이 비어 있다면 그냥 스킵한다.
  4. 행, 열 모두 1~3의 작업을 수행한다.
  5. 리스트를 정렬하고 가장 첫번째 값이 사전순으로 가장 앞서 있는 낱말이다.

리스트에 추가하는 작업만 수행하므로 LinkedList를 선택했고 ArrayList와 비교해본 결과

LinkedList로 구현한 코드가 조금 더 빨랐다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.LinkedList;

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

        String[] size = br.readLine().split(" ");
        int R = Integer.parseInt(size[0]);
        int C = Integer.parseInt(size[1]);
        char[][] array = new char[R][C];
        for (int i = 0; i < R; i++) {
            array[i] = br.readLine().toCharArray();
        }
        LinkedList<String> list = new LinkedList<>();
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < R; i++) {
            buf.setLength(0);
            for (int j = 0; j < C; j++) {
                if (array[i][j] == '#') {
                    if (buf.length() != 0) {
                        if (buf.length() > 1) list.add(buf.toString());
                        buf.setLength(0);
                    }
                } else {
                    buf.append(array[i][j]);
                }
            }
            if (buf.length() > 1) list.add(buf.toString());
        }
        for (int i = 0; i < C; i++) {
            buf.setLength(0);
            for (int j = 0; j < R; j++) {
                if (array[j][i] == '#') {
                    if (buf.length() != 0) {
                        if (buf.length() > 1) list.add(buf.toString());
                        buf.setLength(0);
                    }
                } else {
                    buf.append(array[j][i]);
                }
            }
            if (buf.length() > 1)
                list.add(buf.toString());
        }

        Collections.sort(list);
        System.out.println(list.get(0));
    }
}

문제

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

1 초 512 MB 22928 8320 5196 32.708%

문제

섬으로 이루어진 나라가 있고, 모든 섬을 다리로 연결하려고 한다. 이 나라의 지도는 N×M 크기의 이차원 격자로 나타낼 수 있고, 격자의 각 칸은 땅이거나 바다이다.

섬은 연결된 땅이 상하좌우로 붙어있는 덩어리를 말하고, 아래 그림은 네 개의 섬으로 이루어진 나라이다. 색칠되어있는 칸은 땅이다.

다리는 바다에만 건설할 수 있고, 다리의 길이는 다리가 격자에서 차지하는 칸의 수이다. 다리를 연결해서 모든 섬을 연결하려고 한다. 섬 A에서 다리를 통해 섬 B로 갈 수 있을 때, 섬 A와 B를 연결되었다고 한다. 다리의 양 끝은 섬과 인접한 바다 위에 있어야 하고, 한 다리의 방향이 중간에 바뀌면 안된다. 또, 다리의 길이는 2 이상이어야 한다.

다리의 방향이 중간에 바뀌면 안되기 때문에, 다리의 방향은 가로 또는 세로가 될 수 밖에 없다. 방향이 가로인 다리는 다리의 양 끝이 가로 방향으로 섬과 인접해야 하고, 방향이 세로인 다리는 다리의 양 끝이 세로 방향으로 섬과 인접해야 한다.

섬 A와 B를 연결하는 다리가 중간에 섬 C와 인접한 바다를 지나가는 경우에 섬 C는 A, B와 연결되어있는 것이 아니다.

아래 그림은 섬을 모두 연결하는 올바른 2가지 방법이고, 다리는 회색으로 색칠되어 있다. 섬은 정수, 다리는 알파벳 대문자로 구분했다.

다리가 교차하는 경우가 있을 수도 있다. 교차하는 다리의 길이를 계산할 때는 각 칸이 각 다리의 길이에 모두 포함되어야 한다. 아래는 다리가 교차하는 경우와 기타 다른 경우에 대한 2가지 예시이다.

나라의 정보가 주어졌을 때, 모든 섬을 연결하는 다리 길이의 최솟값을 구해보자.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

출력

모든 섬을 연결하는 다리 길이의 최솟값을 출력한다. 모든 섬을 연결하는 것이 불가능하면 -1을 출력한다.

제한

  • 1 ≤ N, M ≤ 10
  • 3 ≤ N×M ≤ 100
  • 2 ≤ 섬의 개수 ≤ 6

풀이

문제요약

  1. 모든 섬을 연결하는 다리 길이의 최솟값을 구해야 함
  2. 다리의 방향은 변경될 수 없고, 길이는 2개 이상이어야 한다.
  3. 상하좌우로 붙어있다면 같은 섬이다.
  4. 섬의 경우 1, 바다의 경우 0으로 입력이 주어진다.

접근 아이디어

  1. 각 섬들별로 식별가능한 인덱스 부여 (BFS 탐색)
  2. 다리를 놓을 수 있는 모든 경우 탐색 (완전탐색)
  3. 연결 가능한 간선의 최소값 구하기 (최소 스패닝 트리 - 크루스칼)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    static int[] dx = new int[] {-1,1,0,0};
    static int[] dy = new int[] {0,0,-1,1};
    static ArrayList<int[]> bridges = new ArrayList<>();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int answer = 0;
        String[] input = br.readLine().split(" ");

        int N = Integer.parseInt(input[0]);
        int M = Integer.parseInt(input[1]);
        int[][] graph = new int[N][M];
        boolean[][] visited = new boolean[N][M];
        for (int i = 0; i < N; i++) {
            String[] inputs = br.readLine().split(" ");
            for (int j = 0; j < M; j++) {
                graph[i][j] = Integer.parseInt(inputs[j]);
            }
        }
        int islandIndex = 1;

        ArrayList<ArrayList<int[]>> lists = new ArrayList<>();
        for (int i = 0; i <= 6; i++) { // 섬의 개수가 6개 이하이므로 6개만 만들면 댐
            lists.add(new ArrayList<int[]>());
        }

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (!visited[i][j] && graph[i][j] == 1) {
                    Queue<int[]> queue = new LinkedList<>();
                    queue.add(new int[] {i,j});
                    visited[i][j] = true;
                    graph[i][j] = islandIndex;
                    lists.get(islandIndex).add(new int[]{i,j});
                    while (!queue.isEmpty()) {
                        int[] value = queue.poll();
                        for (int k = 0; k < dx.length; k++) {
                            int cy = value[0] + dy[k];
                            int cx = value[1] + dx[k];

                            if (cx < 0 || cx >= M || cy < 0 || cy >= N) continue;
                            if (visited[cy][cx] || graph[cy][cx] == 0) continue;
                            int[] newVal = new int[]{cy,cx};
                            queue.add(newVal);
                            graph[cy][cx] = islandIndex;
                            lists.get(islandIndex).add(newVal);
                            visited[cy][cx] = true;
                        }
                    }
                    islandIndex++;
                }
            }
        }

//        // bridge에 현재값, 타겟값, 거리
        for (int i = 0; i < lists.size(); i++) {
            ArrayList<int[]> islands = lists.get(i);
            for (int j = 0; j < islands.size(); j++) {
                int[] island = islands.get(j);
                for (int k = island[0]-1; k > 0; k--) { // 위쪽 탐색
                    if (graph[k][island[1]] == graph[island[0]][island[1]])
                        break;
                    if (graph[k][island[1]] != 0) { // 0 이 아니고, 현재 값과 다르다면
                        if (island[0] - k == 2)
                            break;
                        int[] bridge = new int[3];
                        bridge[0] = graph[island[0]][island[1]];
                        bridge[1] = graph[k][island[1]];
                        bridge[2] = island[0] - k - 1;
                        bridges.add(bridge);
                        break;
                    }
                }
                for (int k = island[0]+1; k < N; k++) { // 아래쪽 탐색
                    if (graph[k][island[1]] == graph[island[0]][island[1]])
                        break;
                    if (graph[k][island[1]] != 0) { // 0 이 아니고, 현재 값과 다르다면
                        if (k - island[0] == 2)
                            break;
                        int[] bridge = new int[3];
                        bridge[0] = graph[island[0]][island[1]];
                        bridge[1] = graph[k][island[1]];
                        bridge[2] = k - island[0] - 1;
                        bridges.add(bridge);
                        break;
                    }
                }
                for (int k = island[1]-1; k > 0; k--) { // 왼쪽 탐색

                    if (graph[island[0]][k] == graph[island[0]][island[1]])
                        break;
                    if (graph[island[0]][k] != 0) { // 0 이 아니고, 현재 값과 다르다면
                        if (island[1] - k == 2) 
                            break;
                        int[] bridge = new int[3];
                        bridge[0] = graph[island[0]][island[1]];
                        bridge[1] = graph[island[0]][k];
                        bridge[2] = island[1] - k - 1;
                        bridges.add(bridge);
                        break;
                    }
                }
                for (int k = island[1]+1; k < M; k++) { // 오른쪽 탐색

                    if (graph[island[0]][k] == graph[island[0]][island[1]])
                        break;
                    if (graph[island[0]][k] != 0) { // 0 이 아니고, 현재 값과 다르다면
                        if (k - island[1] == 2)
                            break;
                        int[] bridge = new int[3];
                        bridge[0] = graph[island[0]][island[1]];
                        bridge[1] = graph[island[0]][k];
                        bridge[2] = k - island[1] - 1;
                        bridges.add(bridge);
                        break;
                    }
                }
            }
        }
        // 간선의 값을 기준으로 연결 시키기
        Collections.sort(bridges, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[2] - o2[2];
            }
        });
        int[] arr = new int[islandIndex];
        for (int i = 1; i < arr.length; i++) {
            arr[i] = i;
        }
        for (int i = 0; i < bridges.size(); i++) {
            int[] bridge = bridges.get(i);
            int a = find(arr, bridge[0]);
            int b = find(arr, bridge[1]);
            if (a == b) continue;
            union(arr, a, b);
            answer += bridge[2];
        }
        int parent = find(arr, arr[1]);
        for (int i = 1; i < arr.length; i++) {
            if (find(arr, i) != parent) {
                answer = -1;
                break;
            }
        }
        if (answer == 0)
            answer = -1;
        System.out.println(answer);
    }
    public static int find(int[] array, int x) {
        // 내 루트노드 찾기
        if(array[x] == x) return x;
        return array[x] = find(array, array[x]);
    }
    public static void union(int[] array, int x, int y) {
        int a = find(array, x);
        int b = find(array, y);
        if(a==b)return;
        array[b] = a;
    }
}

문제

두 전봇대 A와 B 사이에 하나 둘씩 전깃줄을 추가하다 보니 전깃줄이 서로 교차하는 경우가 발생하였다. 합선의 위험이 있어 이들 중 몇 개의 전깃줄을 없애 전깃줄이 교차하지 않도록 만들려고 한다.

예를 들어, <그림 1>과 같이 전깃줄이 연결되어 있는 경우 A의 1번 위치와 B의 8번 위치를 잇는 전깃줄, A의 3번 위치와 B의 9번 위치를 잇는 전깃줄, A의 4번 위치와 B의 1번 위치를 잇는 전깃줄을 없애면 남아있는 모든 전깃줄이 서로 교차하지 않게 된다. 

 

전깃줄이 전봇대에 연결되는 위치는 전봇대 위에서부터 차례대로 번호가 매겨진다. 전깃줄의 개수와 전깃줄들이 두 전봇대에 연결되는 위치의 번호가 주어질 때, 남아있는 모든 전깃줄이 서로 교차하지 않게 하기 위해 없애야 하는 최소 개수의 전깃줄을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에는 두 전봇대 사이의 전깃줄의 개수가 주어진다. 전깃줄의 개수는 100,000 이하의 자연수이다. 둘째 줄부터 한 줄에 하나씩 전깃줄이 A전봇대와 연결되는 위치의 번호와 B전봇대와 연결되는 위치의 번호가 차례로 주어진다. 위치의 번호는 500,000 이하의 자연수이고, 같은 위치에 두 개 이상의 전깃줄이 연결될 수 없다. 

출력

첫째 줄에 남아있는 모든 전깃줄이 서로 교차하지 않게 하기 위해 없애야 하는 전깃줄의 최소 개수를 출력한다. 둘째 줄부터 한 줄에 하나씩 없애야 하는 전깃줄의 A전봇대에 연결되는 위치의 번호를 오름차순으로 출력한다. 만약 답이 두 가지 이상이라면 그 중 하나를 출력한다.

 

풀이

선택 알고리즘

가장 긴 증가하는 부분 수열 : 전에 풀었던 꼬인 전깃줄과 동일한 알고리즘을 사용하면 된다. 그러나 잘라야하는 전깃줄의 개수와 함께 잘라야하는 인덱스도 출력해야 함.

  • 전깃줄의 간선 수와 인덱스만 주어지므로, 전깃줄과 전깃줄 사이에 서로 연결되지 않은 전깃줄이 있을 수 있다.

가장 긴 증가하는 부분 수열의 인덱스 찾기

TestCase를 통해 찾아보자.

왼쪽에 주어지는 값이 왼쪽 전봇대 인덱스, 오른쪽에 주어지는 값이 오른쪽 전봇대 인덱스이다.

나는 왼쪽 값을 기준으로 먼저 정렬해줬다.

8
1 8
3 9
2 2
4 1
6 4
10 10
9 7
7 6

//  왼쪽 값 기준으로 정렬하기
1 4 
2 2
4 6
6 7
7 9
8 1
9 3
10 10

이 상태에서 가장 긴 증가하는 부분 수열을 구해보자.

DP 배열과 함께, DP 배열의 몇번째 인덱스가 업데이트 되었는지 기록하는 배열을 추가한다.

index = 0

dp 4

index 1              

index = 1

dp 2

index 1 1            

index = 2

dp 2 6

index 1 1 2          

index = 3

dp 2 6 7

index 1 1 2 3        

index = 4

dp 2 6 7 9

index 1 1 2 3 4      

index = 5

dp 1 6 7 9

index 1 1 2 3 4 1    

index = 6

dp 1 3 7 9

index 1 1 2 3 4 1 2  

index = 7

dp 1 3 7 9 10

index 1 1 2 3 4 1 2 5

여기까지 구했다면, 원본 배열과 index 배열을 비교해보자.

원본배열 4 2 6 7 9 1 3 10

index 1 1 2 3 4 1 2 5

원본배열의 끝부터 index 값을 보면 2,6,7,9,10 이라는 배열이 나오는데, 이 배열을 제외한 나머지의 값이 우리가 잘라야 하는 전봇대의 인덱스임을 알 수 있다.

  • 만약 가장 긴 증가하는 부분 수열의 인덱스 또는 증가하는 부분 수열의 값에 포함되지 않는 인덱스를 구해야 할 경우 dp배열의 몇번째 인덱스가 업데이트 되는지 확인해보자…
  • 너무어렵다..

코드

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

public class Main {
    public static class Pair implements Comparable<Pair>{
        int first;
        int second;
        public Pair(int first, int second) {
            this.first = first;
            this.second = second;
        }
        @Override
        public int compareTo(Pair o) {
            return this.first - o.first;
        }
    }
    public static int search(int start, int end, int target, int[] arr) {
        int mid;
        while (start<end) {
            mid = (start + end) / 2;

            if (arr[mid]<target) {
                start = mid+1;
            } else {
                end = mid;
            }
        }
        return end;
    }

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

        int N = Integer.parseInt(st.nextToken());
        int max = 0;
        ArrayList<Pair> arrayList = new ArrayList<>();

        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            int b = Integer.parseInt(st.nextToken());
            int a = Integer.parseInt(st.nextToken());
            arrayList.add(new Pair(a,b));
            max = Math.max(max, b);
        }
        Collections.sort(arrayList);
        int[] dp = new int[max+1];
        int[] target = new int[max+1];
        int index = 1;
        int tIndex = 1;
        // 최장 증가 부분수열 구하기
        for (int i = 0; i < N; i++) {
            if (arrayList.get(i).second > dp[index-1]) { // DP의 마지막 값 보다 array 값이 크면 dp 마지막 인덱스 업데이트
                dp[index] = arrayList.get(i).second;
                target[tIndex++] = index;
                index+=1;

            } else { // DP의 마지막 값보다 arr[i]가 작다면, arr[i]값이 들어갈 위치를 찾아준다.
                int idx = search(1, index-1, arrayList.get(i).second, dp);
                dp[idx] = arrayList.get(i).second;
                target[tIndex++] = idx;
            }
        }
        sb.append(max-index-1).append("\\n");
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();

        for (int i = N; i > 0; i--) {
            if (target[i] != index-1)
                priorityQueue.add(arrayList.get(i-1).second);
            else index--;

        }
        while (!priorityQueue.isEmpty()) {
            sb.append(priorityQueue.poll()).append("\\n");
        }
        System.out.println(sb);
    }
}

문제

유명한 제빵사 김원웅은 빵집을 운영하고 있다. 원웅이의 빵집은 글로벌 재정 위기를 피해가지 못했고, 결국 심각한 재정 위기에 빠졌다.

원웅이는 지출을 줄이고자 여기저기 지출을 살펴보던 중에, 가스비가 제일 크다는 것을 알게되었다. 따라서 원웅이는 근처 빵집의 가스관에 몰래 파이프를 설치해 훔쳐서 사용하기로 했다.

빵집이 있는 곳은 R*C 격자로 표현할 수 있다. 첫째 열은 근처 빵집의 가스관이고, 마지막 열은 원웅이의 빵집이다.

원웅이는 가스관과 빵집을 연결하는 파이프를 설치하려고 한다. 빵집과 가스관 사이에는 건물이 있을 수도 있다. 건물이 있는 경우에는 파이프를 놓을 수 없다.

가스관과 빵집을 연결하는 모든 파이프라인은 첫째 열에서 시작해야 하고, 마지막 열에서 끝나야 한다. 각 칸은 오른쪽, 오른쪽 위 대각선, 오른쪽 아래 대각선으로 연결할 수 있고, 각 칸의 중심끼리 연결하는 것이다.

원웅이는 가스를 되도록 많이 훔치려고 한다. 따라서, 가스관과 빵집을 연결하는 파이프라인을 여러 개 설치할 것이다. 이 경로는 겹칠 수 없고, 서로 접할 수도 없다. 즉, 각 칸을 지나는 파이프는 하나이어야 한다.

원웅이 빵집의 모습이 주어졌을 때, 원웅이가 설치할 수 있는 가스관과 빵집을 연결하는 파이프라인의 최대 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 R과 C가 주어진다. (1 ≤ R ≤ 10,000, 5 ≤ C ≤ 500)

다음 R개 줄에는 빵집 근처의 모습이 주어진다. '.'는 빈 칸이고, 'x'는 건물이다. 처음과 마지막 열은 항상 비어있다.

출력

첫째 줄에 원웅이가 놓을 수 있는 파이프라인의 최대 개수를 출력한다.

풀이

선택 알고리즘

DFS : R*C 배열이 주어졌을 때, i,0 인덱스가 근처 빵집이고, i,C 인덱스가 원웅이의 빵집이다. 따라서, i,0부터 i,C 까지 이동할 수 있는 모든 경우를 탐색하기 때문에 DFS를 선택했다.

시간 복잡도

R개의 경우의수가 있고, depth가 C가 될때까지 반복하며, 3개의 방향으로 탐색

= 3RC

풀이

  1. 근처 빵집에서 원웅이 빵집까지 이동할 수 있는 방법은 오른쪽 위 대각선, 오른쪽, 오른쪽 아래 대각선 으로 세가지 경우가 있다. 따라서 오른쪽으로 한칸 이동 시 마다 세가지의 경우로 탐색을 시도한다.
  2. 이미 이동했던 경로는 다시 이동할 수 없기 때문에 방문 배열을 생성하여 관리 해준다.
  3. 세가지 이동 경우 중 그림을 그려 봤을 때 최대한 위쪽에 붙어서 이동해야 다음 이동 시에 갈 수 있는 경우를 최대로 할 수 있을 것이라고 생각함.

코드

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

public class Main {
    static int R;
    static int C;
    static char[][] graph;
    static boolean[][] visited;
    static int answer = 0;
    static boolean flag = false;
    static int[] dy = new int[] {-1, 0, 1};
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] size = br.readLine().split(" ");

        R = Integer.parseInt(size[0]);
        C = Integer.parseInt(size[1]);

        graph = new char[R][C];
        visited = new boolean[R][C];
        for (int i = 0; i < R; i++) {
            graph[i] = br.readLine().toCharArray();
        }

        for (int i = 0; i < R; i++) {
            visited[i][0] = true; // 0번 부터 시작
            dfs(i, 0,0); // 도달 가능한지 탐색
            flag = false; // 플래그 초기화
        }
        System.out.println(answer);
    }
    public static void dfs(int y, int x, int depth) {
        if (depth == C-1) {
            answer++; // 끝까지 도달했다면 답 1추가
            flag = true; // 끝까지 도달했으므로 더이상 탐색하지 말고 돌아오도록 플래그 설정
            return;
        }

        for (int i = 0; i < dy.length; i++) {
            if (flag) // 플래그 설정되었다면 더이상 탐색하지 않고 돌아와
                break;
            int currentX = x + 1; 
            int currentY = y + dy[i]; // 3방향으로 탐색

            if (currentY < 0 || currentY >= R) continue;
            if (visited[currentY][currentX] || graph[currentY][currentX] == 'x') continue;
            visited[currentY][currentX] = true;
            dfs(currentY, currentX, depth + 1);
        }
    }
}
문제

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

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

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

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

각 나라의 인구수가 주어졌을 때, 인구 이동이 며칠 동안 발생하는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N, L, R이 주어진다. (1 ≤ N ≤ 50, 1 ≤ L ≤ R ≤ 100)

둘째 줄부터 N개의 줄에 각 나라의 인구수가 주어진다. r행 c열에 주어지는 정수는 A[r][c]의 값이다. (0 ≤ A[r][c] ≤ 100)

인구 이동이 발생하는 일수가 2,000번 보다 작거나 같은 입력만 주어진다.

출력

인구 이동이 며칠 동안 발생하는지 첫째 줄에 출력한다.

풀이

선택 알고리즘

BFS - 국경선을 이을 수 있는지 여부는 2차원배열의 상하좌우로 탐색하는 방식에 유리한 BFS를 선정함

시간 복잡도

전체 후보를 모두 방문해야 하고, NxN 배열을 방문하므로 N제곱 X N제곱 (BFS)

N의 네제곱이므로, 50 X 50 X 50 X 50 = 6,250,000으로 계산

구현

  1. 모든 땅을 방문하여, 국경선을 열 수 있는 경우, 국경선을 공유 가능한 모든 땅의 좌표를 arrayList에 담아서 보관하고, 탐색이 끝났다면 큐에 보관한 어레이리스트를 저장 → 인구 이동이 일어나는 경우가 하루에 여러 땅에서 일어날 수 있기 때문에 각각 관리해줘야 한다.
  2. 큐가 비어있지 않다면 어레이리스트를 꺼내서 국경선을 공유하는 모든 땅의 값을 업데이트 해줌
  3. 만약 그 어디도 방문하지 못했다면 (국경선을 공유할 수 있는 땅이 없다면), 종료한다.

코드

package com.company.baekjoon;

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

public class BaekJoon16234 {
    static int N;
    static int L;
    static int R;
    static int[][] arr;
    static boolean[][] union;
    static Queue<int[]> queue;
    static Queue<ArrayList<int[]>> a = new LinkedList<>();
    static ArrayList<int[]> arrayList = new ArrayList<>();
    static int[] dx = new int[] { -1, 1, 0, 0};
    static int[] dy = new int[] { 0, 0, -1, 1};
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        N = Integer.parseInt(st.nextToken());
        L = Integer.parseInt(st.nextToken());
        R = Integer.parseInt(st.nextToken());
        int answer = 0;
        arr = new int[N][N];
        union = new boolean[N][N];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        queue = new LinkedList<>();

        while (true) {
           for (boolean booleans[]: union) {
                Arrays.fill(booleans, false);
            }
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (!union[i][j]) {
                        queue.add(new int[]{i,j});
                        bfs();
                        if (arrayList.size() > 0) {
                            a.add((ArrayList<int[]>) arrayList.clone());
                            arrayList.clear();
                        }
                    }
                }
            }
            while (!a.isEmpty()) {
                ArrayList<int[]> list = a.poll();

                int size = list.size();
                int sum = 0;

                if (size == 0) { // 인구이동 후보가 없음
                    break;
                }

                for (int k = 0; k < size; k++) {
                    int[] n = list.get(k);
                    sum += arr[n[0]][n[1]];
                }
                int value = sum / size;
                for (int k = 0; k < size; k++) {
                    int[] n = list.get(k);
                    arr[n[0]][n[1]] = value;
                }
            }

            arrayList.clear();

            // 만약 더이상 진행할 수 없을 경우 (아무 지역도 후보가 될 수 없는 경우)
            boolean isContinue = false;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (union[i][j])
                        isContinue = true;
                }
            }
            if (!isContinue)
                break;

            // 모든 나라 검사 다 했으면 1 증가함.
            answer++;

        }
        System.out.println(answer);
    }
    public static void bfs() {
        while (!queue.isEmpty()) {
            int[] nation = queue.poll();
            for (int i = 0; i < dx.length; i++) {
                int currentX = nation[0] + dx[i];
                int currentY = nation[1] + dy[i];

                if (currentX < 0 || currentX >= N || currentY < 0 || currentY >= N) continue;
                if (union[currentX][currentY]) continue; // 이미 방문한경우
                if (Math.abs(arr[nation[0]][nation[1]] - arr[currentX][currentY]) >= L &&
                        Math.abs(arr[nation[0]][nation[1]] - arr[currentX][currentY]) <= R) { // 국경이 열림
                    if (!union[nation[0]][nation[1]]) {
                        union[nation[0]][nation[1]] = true;
                        arrayList.add(new int[]{nation[0], nation[1]});
                    }
                    union[currentX][currentY] = true;
                    queue.add(new int[]{currentX, currentY});
                    arrayList.add(new int[]{currentX, currentY});
                }
            }
        }
    }
}

 

문제

문제

오목은 바둑판에 검은 바둑알과 흰 바둑알을 교대로 놓아서 겨루는 게임이다. 바둑판에는 19개의 가로줄과 19개의 세로줄이 그려져 있는데 가로줄은 위에서부터 아래로 1번, 2번, ... ,19번의 번호가 붙고 세로줄은 왼쪽에서부터 오른쪽으로 1번, 2번, ... 19번의 번호가 붙는다.

 

위의 그림에서와 같이 같은 색의 바둑알이 연속적으로 다섯 알을 놓이면 그 색이 이기게 된다. 여기서 연속적이란 가로, 세로 또는 대각선 방향 모두를 뜻한다. 즉, 위의 그림은 검은색이 이긴 경우이다. 하지만 여섯 알 이상이 연속적으로 놓인 경우에는 이긴 것이 아니다.

입력으로 바둑판의 어떤 상태가 주어졌을 때, 검은색이 이겼는지, 흰색이 이겼는지 또는 아직 승부가 결정되지 않았는지를 판단하는 프로그램을 작성하시오. 단, 검은색과 흰색이 동시에 이기거나 검은색 또는 흰색이 두 군데 이상에서 동시에 이기는 경우는 입력으로 들어오지 않는다.

입력

19줄에 각 줄마다 19개의 숫자로 표현되는데, 검은 바둑알은 1, 흰 바둑알은 2, 알이 놓이지 않는 자리는 0으로 표시되며, 숫자는 한 칸씩 띄어서 표시된다.

출력

첫줄에 검은색이 이겼을 경우에는 1을, 흰색이 이겼을 경우에는 2를, 아직 승부가 결정되지 않았을 경우에는 0을 출력한다. 검은색 또는 흰색이 이겼을 경우에는 둘째 줄에 연속된 다섯 개의 바둑알 중에서 가장 왼쪽에 있는 바둑알(연속된 다섯 개의 바둑알이 세로로 놓인 경우, 그 중 가장 위에 있는 것)의 가로줄 번호와, 세로줄 번호를 순서대로 출력한다.

풀이

4가지 방향 배열 이용.

오목알이 대각선 위, 오른쪽, 대각선 아래, 아래 방향으로 있는지 확인.

만약 오목알이 5개가 다 맞춰졌다면, 반대방향에 같은 오목이 있는지 확인

코드

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

public class BaekJoon2615 {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int length = 19;
		String[][] arr = new String[length][length];

		
		for (int i = 0; i < length; i++) {
			arr[i] = br.readLine().split(" ");
		}
		int[] dx = {1,1,0,1};
		int[] dy = {-1,0,1,1};
		boolean isFound = false;
		for (int i = 0; i < length; i++) {
			if (isFound) // 찾았으면 탈출
				break;
			for (int j = 0; j < length; j++) {
				if (isFound) // 찾았으면 탈출
					break;
				if (!arr[i][j].equals("0")) { // 빈 칸이 아니라면 검사
					String target = arr[i][j];
					for (int k = 0; k < dy.length; k++) { // 방향검사
						int currentX = j + dx[k];
						int currentY = i + dy[k];
						
						int count = 0;
						if (currentX < 0 || currentX >= length || currentY < 0 || currentY >= length) {
							continue;
						}
						if (arr[currentY][currentX].equals(target)) { // 진행 방향에 같은 돌이 있다면
							for (int l = 1; l <= 5; l++) { // 5개 있는지 검사
								int cX = currentX + (dx[k] * l);
								int cY = currentY + (dy[k] * l);
								if (cX < 0 || cX >= length || cY < 0 || cY >= length) {
									break;
								}
								if (arr[cY][cX].equals(target)) {
									count++;
								} else
									break;
							}	
						}
						if (count == 3) { // 5개 있다면 
							int x1 = j + -dx[k];
							int y1 = i + -dy[k];
							if (x1 >= 0 && y1 >= 0 && arr[y1][x1].equals(target)) { // 이전 인덱스 비교해서 6개이상인지 확인
								break;
							}
							System.out.println(target + "\\n" + (i+1) + " " + (j+1));
							isFound = true;
						}
					}
				}
			}
		}
		if (!isFound)
			System.out.println("0");
		
	}
}

문제

문제

자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.

  • 1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열

입력

첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)

출력

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.

수열은 사전 순으로 증가하는 순서로 출력해야 한다.

풀이

DFS 이용하여 모든 인덱스 순회 함.

코드

import java.util.Scanner;

public class Main {
    static StringBuilder sb = new StringBuilder();
    static StringBuilder result = new StringBuilder();
    public static void dfs(int[] arr, boolean[] visited, int depth, int index, int d) {
        visited[index] = true;
        sb.append(arr[index]+" ");
        if (depth == 1)  {
            result.append(sb + "\n");
            return;
        }
        for (int i = 0; i < visited.length; i++) {
            if (!visited[i]) {
                dfs(arr, visited, depth-1, i, d);
                visited[i] = false;
                sb.delete(sb.length()-2, sb.length());
            }
        }

    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int a = sc.nextInt();
        int b = sc.nextInt();

        int[] arr = new int[a];
        boolean[] visited = new boolean[a];
        for (int i = 0; i < a; i++) {
            arr[i] = i+1;
        }

        for (int i = 0; i < visited.length; i++) {
            if (!visited[i]) {
                dfs(arr, visited, b, i, b);
                visited[i] = false;
                sb.setLength(0);
            }
        }
        System.out.println(result);
    }
}

StringBuilder 변수를 두개 이용하여 출력을 1회로 수정 후 시간이 1220ms → 268ms 로 많이 줄었다.

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 128 MB 53622 11601 8619 19.930%
문제
1부터 연속적으로 번호가 붙어있는 스위치들이 있다. 스위치는 켜져 있거나 꺼져있는 상태이다. <그림 1>에 스위치 8개의 상태가 표시되어 있다. ‘1’은 스위치가 켜져 있음을, ‘0’은 꺼져 있음을 나타낸다. 그리고 학생 몇 명을 뽑아서, 학생들에게 1 이상이고 스위치 개수 이하인 자연수를 하나씩 나누어주었다. 학생들은 자신의 성별과 받은 수에 따라 아래와 같은 방식으로 스위치를 조작하게 된다.

남학생은 스위치 번호가 자기가 받은 수의 배수이면, 그 스위치의 상태를 바꾼다. 즉, 스위치가 켜져 있으면 끄고, 꺼져 있으면 켠다. <그림 1>과 같은 상태에서 남학생이 3을 받았다면, 이 학생은 <그림 2>와 같이 3번, 6번 스위치의 상태를 바꾼다.

여학생은 자기가 받은 수와 같은 번호가 붙은 스위치를 중심으로 좌우가 대칭이면서 가장 많은 스위치를 포함하는 구간을 찾아서, 그 구간에 속한 스위치의 상태를 모두 바꾼다. 이때 구간에 속한 스위치 개수는 항상 홀수가 된다.

스위치 번호 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧
스위치 상태 0 1 0 1 0 0 0 1
<그림 1>

예를 들어 <그림 2>에서 여학생이 3을 받았다면, 3번 스위치를 중심으로 2번, 4번 스위치의 상태가 같고 1번, 5번 스위치의 상태가 같으므로, <그림 3>과 같이 1번부터 5번까지 스위치의 상태를 모두 바꾼다. 만약 <그림 2>에서 여학생이 4를 받았다면, 3번, 5번 스위치의 상태가 서로 다르므로 4번 스위치의 상태만 바꾼다.

스위치 번호 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧
스위치 상태 0 1 1 1 0 1 0 1
<그림 2>

스위치 번호 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧
스위치 상태 1 0 0 0 1 1 0 1
<그림 3>

입력으로 스위치들의 처음 상태가 주어지고, 각 학생의 성별과 받은 수가 주어진다. 학생들은 입력되는 순서대로 자기의 성별과 받은 수에 따라 스위치의 상태를 바꾸었을 때, 스위치들의 마지막 상태를 출력하는 프로그램을 작성하시오.

입력
첫째 줄에는 스위치 개수가 주어진다. 스위치 개수는 100 이하인 양의 정수이다. 둘째 줄에는 각 스위치의 상태가 주어진다. 켜져 있으면 1, 꺼져있으면 0이라고 표시하고 사이에 빈칸이 하나씩 있다. 셋째 줄에는 학생수가 주어진다. 학생수는 100 이하인 양의 정수이다. 넷째 줄부터 마지막 줄까지 한 줄에 한 학생의 성별, 학생이 받은 수가 주어진다. 남학생은 1로, 여학생은 2로 표시하고, 학생이 받은 수는 스위치 개수 이하인 양의 정수이다. 학생의 성별과 받은 수 사이에 빈칸이 하나씩 있다.

출력
스위치의 상태를 1번 스위치에서 시작하여 마지막 스위치까지 한 줄에 20개씩 출력한다. 예를 들어 21번 스위치가 있다면 이 스위치의 상태는 둘째 줄 맨 앞에 출력한다. 켜진 스위치는 1, 꺼진 스위치는 0으로 표시하고, 스위치 상태 사이에 빈칸을 하나씩 둔다.

풀이

입력받은 학생의 성별에 따라 조건 분기

  • 남자 : 입력받은 수의 배수에 해당하는 인덱스 모두 토글
  • 여자 : 입력받은 수를 중심으로 -1, +1 인덱스가 동일한 값일 경우 토글

중요한거

  1. 여자의 경우 입력받은 수 자기자신도 토글 해줘야 함.
  2. 스위치 개수가 20이 넘으면 아래줄에 출력
  3. 마지막 출력의 경우 띄어쓰기 안해야 함

코드

package com.company.baekjoon;

import java.util.Scanner;

public class BaekJoon1244 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        StringBuilder sb = new StringBuilder();
        int n = sc.nextInt();
        boolean[] arr = new boolean[n+1];
        for (int i = 1; i <= n; i++) {
            arr[i] = sc.nextInt() > 0;
        }
        int s = sc.nextInt();
        for (int i = 0; i < s; i++) {
            int gender = sc.nextInt();
            int idx = sc.nextInt();

            if (gender == 1) {
                for (int j = idx; j < arr.length; j+=idx) {
                    if (j % idx == 0)
                        arr[j] = !arr[j];
                }
            } else {
                int a = 1;
                arr[idx] = !arr[idx];
                while(true) {
                    if (idx - a < 1 || idx + a > n) break;
                    if (arr[idx+a] == arr[idx-a]) {
                        arr[idx+a] = !arr[idx+a];
                        arr[idx-a] = !arr[idx-a];
                        a++;
                    } else
                        break;
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            int value = arr[i] ? 1 : 0;
            if (i != n)
                sb.append(value + " "); // 이거때문에 틀림
            else
                sb.append(value);

            if (i % 20 == 0)
                sb.append("\n");
        }
        System.out.println(sb);
    }
}

문제

시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
2 초 512 MB 1081 460 346 41.587%
문제
영선이는 이번에 편의점으로 창업을 하려고 계획 중이다. 이번 창업을 위해 많은 준비를 하고 있는데, 아직 편의점을 세울 위치를 결정을 하지 못했다. 영선이는 미리 시장조사를 하여, 주요 고객들이 어느 위치에 존재하는지 파악을 하였고, 모든 고객들의 거리의 합을 최소로 하려한다. 두 위치의 거리는 |x1-x2|+|y1-y2|로 정의한다.

n명의 주요 고객들의 위치 (xi,yi)이 주어질 때, 모든 고객들의 거리 합을 최소로 하는 위치에 편의점을 세울 때, 그 최소 거리 합을 출력하시오.

입력
첫째 줄에는 주요 고객들의 수n이 주어진다.(1≤n≤100,000)

다음 n줄에는 고객들의 위치 (x,y)가 주어진다.(-1,000,000≤x,y≤1,000,000)

출력
모든 고객들의 거리 합을 최소로 하는 위치에 편의점을 세울 때, 그 최소 거리 합을 출력하시오.

풀이

  1. 첫번째로 들어간 차는 추월이 불가능하다.
  2. 확인대상 차량의 index보다 앞에 있던 차량 중 하나라도 추월 했다면 추월 차량이다.

코드

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

public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int answer = 0;
        int N = Integer.parseInt(br.readLine());
        String[] s = new String[N];
        ArrayList<String> comp = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            s[i] = br.readLine();
        }
        for (int i = 0; i < N; i++) {
            comp.add(br.readLine());
        }

        for (int i = 1; i < N; i++) { // 0번 차량은 확인 불필요
            for (int j = 0; j < i; j++) { // i번 차량보다 먼저 들어간 차량만 확인 대상
                int compIndex = comp.indexOf(s[j]);
                int index = comp.indexOf(s[i]);
                if (compIndex > index) {
                    answer++;
                    break;
                }
            }
        }
        System.out.println(answer);
    }
}

문제

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

 

2 초 512 MB 1081 460 346 41.587%

문제

영선이는 이번에 편의점으로 창업을 하려고 계획 중이다. 이번 창업을 위해 많은 준비를 하고 있는데, 아직 편의점을 세울 위치를 결정을 하지 못했다. 영선이는 미리 시장조사를 하여, 주요 고객들이 어느 위치에 존재하는지 파악을 하였고, 모든 고객들의 거리의 합을 최소로 하려한다. 두 위치의 거리는 |x1-x2|+|y1-y2|로 정의한다.

n명의 주요 고객들의 위치 (xi,yi)이 주어질 때, 모든 고객들의 거리 합을 최소로 하는 위치에 편의점을 세울 때, 그 최소 거리 합을 출력하시오.

입력

첫째 줄에는 주요 고객들의 수n이 주어진다.(1≤n≤100,000)

다음 n줄에는 고객들의 위치 (x,y)가 주어진다.(-1,000,000≤x,y≤1,000,000)

출력

모든 고객들의 거리 합을 최소로 하는 위치에 편의점을 세울 때, 그 최소 거리 합을 출력하시오.

풀이

맨해튼 거리란 유클리드 거리와 함께 좌표간의 거리를 구하는 방식을 말하는데, 아래의 그림을 봤을 때 빨간색, 파란색, 노란색은 모두 12로 가장 짧은 맨하탄 거리이다. 초록색으로 직선 거리를 표현 한 것이 유클리드 거리이다.

출처 : 위키백과

위 문제에서 두 위치의 거리를 |x1-x2| + |y1-y2|로 표현 했으므로, 이 문제는 최소 맨해튼 거리의 합을 구하는 문제이다.

가장 짧은 거리를 구한다고 했을 때, 먼저 떠오르는 방법은 입력받은 가장 작은 좌표와 가장 큰 좌표의 중간 값이라고 생각했다.

좌표의 맨해튼 거리의 합이 최소가 되는 지점이 될 만한 부분은 두가지가 있었다.

  1. x,y 좌표 각각의 중간 인덱스
  2. x,y 좌표 각각의 합을 좌표의 갯수로 나눈 점

결론적으로 x,y 좌표 각각의 중간 인덱스가 올바른 최소값 이었고, 문제를 나같은 멍청대가리에게도 쉽게 설명해준 Hi군에게 감사 인사를 전한다.

아주 간단하게 1차원 좌표에서 확인 해보면, 5개의 점이 있다고 치자.

0 10 20 30 100 다섯개의 점에서 맨해튼 거리의 합이 최소가 되는 지점은 어디일까

1번 방법으로 값을 구한다면 5 / 2 = 2 로 2번 인덱스인 20이 중간 지점이 될 것이고,

2번 방법으로 값을 구한다면 0 + 10 + 20 + 30 + 100 / 5 로 32가 중간지점이 될 것이다.

그렇다면 이 중간지점을 토대로 각 좌표들의 거리의 합을 구하면 어떻게 될까

1번 방법은 20 + 10 + 0 + 10 + 80 으로 120

2번 방법은 32 + 22 + 12 + 2 + 68 으로 136이라는 값이 나온다.

x,y 좌표 각각의 중간 인덱스를 구한 후 |x1-x2| + |y1-y2| 계산하는 코드를 그대로 옮겨서 풀었다.

코드

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

public class Main {

    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());
        long answer = 0;
        int[][] arr = new int[N][2];
        int[] x = new int[N];
        int[] y = new int[N];

        for (int i = 0; i < N; i++) {
            String[] s = br.readLine().split(" ");
            x[i] = Integer.parseInt(s[0]);
            y[i] = Integer.parseInt(s[1]);
            arr[i][0] = x[i];
            arr[i][1] = y[i];
        }
        Arrays.sort(x);
        Arrays.sort(y);
        int compX = x[N/2];
        int compY = y[N/2];
        for (int i = 0; i < N; i++) {
            answer += Math.abs(arr[i][0] - compX) + Math.abs(arr[i][1] - compY);
        }
        System.out.println(answer);
    }
}

+ Recent posts