https://www.acmicpc.net/problem/2992

 

[난이도]

- Silver 3

 

[알고리즘]

- 백트래킹

 

[코드]

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

public class Main {
    static int N, num, min = Integer.MAX_VALUE;
    static int[] arr, list;
    static boolean[] visited;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        String X = br.readLine();
        num = Integer.parseInt(X);
        N = X.length(); // 입력받은 문자열의 자릿수
        arr = new int[N];
        list = new int[N];
        visited = new boolean[N];
        for (int i = 0; i < N; i++) {
            arr[i] = X.charAt(i) - '0';
        }
        backTracking(0);
        System.out.println(min == Integer.MAX_VALUE ? 0 : min); // min이 여전히 Integer.MAX_VALUE 라면 0 출력 아니라면 min 출력
    }

    static void backTracking(int depth) {
        if (depth == N) {
            // int배열 -> 하나씩 꺼내서 하나의 문자열 -> 정수로 변환
            String s = "";
            for (int i : list) {
                s += i;
            }
            int n = Integer.parseInt(s);

            if (num < n) { // 입력값보다 큰 수중에 최솟값
                min = Math.min(min, n);
            }
            return;
        }
        for (int i = 0; i < N; i++) {
            if (!visited[i]) { // 사용하지 않은 숫자라면 사용
                visited[i] = true; // 사용한 숫자는 체크
                list[depth] = arr[i]; // 새로만들어지는 숫자는 list배열에 담아둠
                backTracking(depth + 1);
                visited[i] = false; // 사용 끝났으면 다시 false
            }
        }
    }
}

 

[풀이]

1. for문을 돌며 각 자리의 숫자를 list에 담아준 후 depth+1 시킨다.

2. depth 가 N이 되었다면 처음에 입력받은 숫자와 비교한다.

3. 비교한 수가 입력값보다 크다면 min을 갱신한다.

4. min이 갱신이안되었다면 0을출력 갱신되었다면 min 출력

 

for문을 돌며 list배열에 담아준것을 꺼내 비교하려면 

list배열에 담겨져있는 수를 하나의 문자열로 변환 후 문자열을 다시 정수로 변환해준다.

 // int배열 -> 하나씩 꺼내서 하나의 문자열 -> 정수로 변환
            String s = "";
            for (int i : list) {
                s += i;
            }
            int n = Integer.parseInt(s);

'코딩테스트' 카테고리의 다른 글

백준 15649: N과 M (1)[JAVA]  (0) 2024.05.07
백준 10974: 모든 순열[JAVA]  (0) 2024.05.04
백준 11060: 점프 점프[JAVA]  (0) 2024.05.03
백준 11060: 점프 점프[JAVA]  (1) 2024.05.02
백준 2644: 촌수계산[JAVA]  (0) 2024.05.02

https://www.acmicpc.net/problem/5568

 

5568번: 카드 놓기

예제 1의 경우 상근이는 11, 12, 21, 112, 121, 122, 212를 만들 수 있다.

www.acmicpc.net

[난이도]

- Silver 4

 

[알고리즘]

- 부르트 포스

- 백트래킹

 

[코드]

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

public class Main {
    static int n, k;
    static boolean[] visited;
    static String[] arr;
    static ArrayList<String> list;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

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

        visited = new boolean[n];
        list = new ArrayList<>();

        arr = new String[n];
        for (int i = 0; i < n; i++) {
            arr[i] = br.readLine();
        }

        dfs(0, "");
        System.out.println(list.size());
    }

    static void dfs(int count, String tmp) {

        if (count == k) { // k개만 뽑는다
            if (!list.contains(tmp)) { // 이미 있는 숫자라면 추가하지 않음
                list.add(tmp);
            }
            return;
        }

        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                dfs(count + 1, tmp+arr[i]);
                visited[i] = false;
            }
        }
    }
}

 

[풀이]

1. dfs 메서드는 한번 탐색할 때 마다 count 를 1 증가시키고, 인덱스의 값을 담는다.

2. count가 k가 될때 까지 반복한다. k가 될 경우 tmp에 담겨있는 값을 list에 추가한다. list에 이미 동일한 값이 있다면 추가하지 않는다.

3. 1, 2를 반복해 완점탐색을 한다.

4. list의 size를 출력한다.

'코딩테스트' 카테고리의 다른 글

백준 14501: 퇴사[JAVA]  (0) 2024.04.17
백준 9290: 틱택토 이기기[JAVA]  (0) 2024.04.16
백준 1639: 행운의 티켓[JAVA]  (0) 2024.04.13
백준 1544: 사이클 단어[JAVA]  (0) 2024.04.12
백준 7568: 덩치[JAVA]  (0) 2024.04.12

https://www.acmicpc.net/problem/1987

 

1987번: 알파벳

세로 $R$칸, 가로 $C$칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 ($1$행 $1$열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의

www.acmicpc.net

 

 

[정답 코드]

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

public class Main {
    static int R, C, answer = 0;
    static char[][] graph;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static HashSet<Character> alphabet;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());
        graph = new char[R][C];

        for (int i = 0; i < R; i++) {
            String input = br.readLine();
            for (int j = 0; j < C; j++) {
                graph[i][j] = input.charAt(j);
            }
        }
        alphabet = new HashSet<>(); // HashSet 초기화
        back(0, 0, 1); //
        System.out.println(answer);
    }
    static void back(int x, int y, int count) {
        answer = Math.max(answer, count);
        alphabet.add(graph[x][y]);
        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (nx >= 0 && nx < R && ny >= 0 && ny < C) {
                if (!alphabet.contains(graph[nx][ny])) { // 처음 방문한 알파벳일 경우
                    back(nx, ny, count + 1);
                }
            }
        }
        alphabet.remove(graph[x][y]); // 이전 상태로 돌아가기 위해 제거
    }
}

 

[설명]

백트래킹 문제이다.

일반적인 백트래킹 알고리즘과 다른점은 단순 방문했던 자리를 체크하는게 아니라 똑같은 알파벳이 아닌자리를 체크해줘야한다. 그 부분은 HashSet을 이용해 체크해주었다. 그리고 alphabet.remove()를 활용해 지나온 알파벳을 지우며 이전상태로 돌아간다.

https://www.acmicpc.net/problem/15686

 

15686번: 치킨 배달

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸

www.acmicpc.net

 

 

[정답 코드]

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

public class Main {
    static int N, M;
    static int[][] graph;
    static ArrayList<int[]> house = new ArrayList<>();
    static ArrayList<int[]> chicken = new ArrayList<>();
    static ArrayList<int[]> selected = new ArrayList<>();
    static int result = Integer.MAX_VALUE;
    static boolean[] visited;
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

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

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine(), " ");
            for (int j = 0; j < N; j++) {
                graph[i][j] = Integer.parseInt(st.nextToken());
                if (graph[i][j] == 1) {
                    house.add(new int[]{i, j}); // 집의 좌표 저장
                } else if (graph[i][j] == 2) {
                    chicken.add(new int[]{i, j}); // 치킨집의 좌표 저장
                }
            }
        }
        visited = new boolean[chicken.size()];

        back(0, 0);
        System.out.println(result); // 출력
    }

    static void back(int depth, int start) {
        if (depth == M) { // M개를 뽑아서 selected 리스트에 M개 저장이 끝났다면
            int sum = 0;
            for (int[] h : house) { // 모든 집들과 치킨집과의 최소거리를 계산
                int min = Integer.MAX_VALUE;
                for (int[] s : selected) { // 선택한 M개의 치킨집과 집의 거리를 계산해 최소거리를 구함
                    int d = Math.abs(h[0] - s[0]) + Math.abs(h[1] - s[1]);
                    min = Math.min(d, min);
                }
                sum += min; // 그렇게 구한 최소거리를 sum에 저장
            }
            result = Math.min(result, sum); // 그렇게 구한 sum들중에 최소값만 저장
            return;
        }

        for (int i = start; i < chicken.size(); i++) { // 모든 치킨집들을 탐색함
            if (!visited[i]) {
                visited[i] = true;
                selected.add(chicken.get(i));
                back(depth + 1, i + 1);
                selected.remove(selected.size() - 1);
                // 탐색이 끝났다면 리스트를 비우기 위한 로직
                // 배열로 했다면 덮어씌울수 있지만 리스트라 제거해줘야함
                visited[i] = false;
            }
        }
    }
}

 

[설명]

백트래킹 알고리즘 : 

1. for문을 돌며 치킨집들중 M개의 치킨집을 골라 selected 리스트에 담는다.

2. selected 리스트에 M개의 치킨집이 저장 되었다면 집과 선택한 치킨집들과의 최소거리를 구한다.

3. 그렇게 구한 각집과 선택한치킨집과의 최소거리를 모두 더했다면

4. 다시 치킨집들 중 아까와 다른 M개의 치킨집을 골라 반복한다.

백준 15649번: N과 M[JAVA]

https://www.acmicpc.net/problem/15649

 

15649번: N과 M (1)

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

www.acmicpc.net

 

[정답 코드]

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

public class Main {
    static int N, M;
    static int[] arr;
    static boolean[] visit;
    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) throws Exception {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        arr = new int[M];
        visit = new boolean[N]; // 기본값 모두 false

        dfs(0);

        System.out.println(sb);
        br.close();
    }

    public static void dfs(int depth) {
        if (depth == M) {
            //depth 가 M까지 도착하면 sb에 집어넣고 return
            for (int i : arr) {
                sb.append(i).append(" ");
            }
            sb.append("\n");
            return;
        }

        for (int i = 0; i < N; i++) {
            if (!visit[i]) {
                visit[i] = true; // 방문한 노드는 true
                arr[depth] = i + 1;
                dfs(depth + 1);
                visit[i] = false; // 탐색이 모두 끝나면 모두 false 로 변경
            }
        }
    }
}

 

[설명]

위 그림을 보면 이해하기가 쉽다. 

입력값으로 3 3 을 입력받아 자연수 1부터 3(N) 까지의 수를 3(M)개 고른것이다.

M개를 골라야하기 때문에 배열 int[M] 을 선언한 후 DFS를 하며 찾아가는것이다.

백준 9663번: N-Queen[JAVA]

https://www.acmicpc.net/problem/9663

 

9663번: N-Queen

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

[정답코드]

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

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

    public static void main(String[] args) throws Exception {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

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

        dfs(0);
        System.out.println(count);

        br.close();
    }

    public static void dfs(int depth) {
        if (depth == N) {
            //depth가 N까지 도착하면 return
            count++;
            return;
        }
        for (int i = 0; i < N; i++) {
            arr[depth] = i;
            if (possible(depth)) {
                // 모든 조건에 통과하면 다음 depth탐색
                dfs(depth + 1);
            }
        }
    }

    public static boolean possible(int col) {
        for (int i = 0; i < col; i++) {
            // 같은 행인지
            if (arr[i] == arr[col]) {
                return false;
            }
            //대각선방향에 존재하는지
            else if (Math.abs(col - i) == Math.abs(arr[col] - arr[i])) {
                return false;
            }
        }
        // 같은행에도 없고 대각선방향에도 없으면 true
        return true;
    }
}

 

[설명]

N-Queen 문제는 대표적인 백트래킹 알고리즘 문제이다. 체스룰을 안다면 알수있듯이 퀸은 상하좌우,대각선방향으로 움질일 수 있다. 문제에서는 서로 공격할 수 없는 경우의 수 이기 때문에 각 퀸들이 같은행, 같은열에 존재하지 않고 해당 위치에서 대각선방향에 존재해서는 안된다.

dfs, 백트래킹 알고리즘으로 풀 수 있다.

 

[그림 해석]

 

먼저 1행1열에 퀸을 놓는다. 그 다음 depth로 간다. 2번째 퀸은 2행1열에 놓지만 같은열에 존재하기 때문에 바로 탐색을 종료한다. 다시 2번째 퀸을 2행2열에 놓아본다. 이번엔 대각선에 위치하기 때문에 탐색을 종료한다. 이번엔 2행3열에 놓아보니 놓을 수 있는 위치이다. 다음 depth로 이동한다. 이를 반복하다 정답을 찾을경우 return 한다.

 

간단히 설명하자면

정답을 찾기위해 depth++ 하며 탐색을 하고, 정답이 아닐경우 다시 돌아온다.

백준 14888번: 연산자 끼워넣기[JAVA]

https://www.acmicpc.net/problem/14888

 

14888번: 연산자 끼워넣기

첫째 줄에 수의 개수 N(2 ≤ N ≤ 11)가 주어진다. 둘째 줄에는 A1, A2, ..., AN이 주어진다. (1 ≤ Ai ≤ 100) 셋째 줄에는 합이 N-1인 4개의 정수가 주어지는데, 차례대로 덧셈(+)의 개수, 뺄셈(-)의 개수, 곱

www.acmicpc.net

 

[정답 코드]

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    static int MAX = Integer.MIN_VALUE;
    static int MIN = Integer.MAX_VALUE;
    static int N; // 숫자 개수
    static int[] arr; // 숫자 배열
    static int[] op = new int[4]; // 연산자 배열
    public static void main(String[] args) throws Exception {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");

        // 숫자 갯수 입력
        N = Integer.parseInt(st.nextToken());

        // 숫자 배열 입력
        arr = new int[N];
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }

        // 연산자 갯수 입력
        st = new StringTokenizer(br.readLine(), " ");
        for (int i = 0; i < 4; i++) {
            op[i] = Integer.parseInt(st.nextToken());
        }

        backTracking(arr[0], 1);

        //최대값 최소값 출력
        bw.write(MAX + "\n" + MIN);

        bw.flush();
        bw.close();
        br.close();
    }

    // 로직
    public static void backTracking(int num, int idx) {

        // idx == N 이 되면 종료, arr 끝까지 탐색했다는 의미
        if (idx == N) {
            MAX = Math.max(MAX, num);
            MIN = Math.min(MIN, num);
            return;
        }

        for (int i = 0; i < 4; i++) {
            // 해당 연산자가 존재하면, 해당연산자 사용한 후 개수 하나 제거
            if (op[i] > 0) {
                op[i]--;
                switch (i) {
                    case 0:	backTracking(num + arr[idx], idx + 1);	break;
                    case 1:	backTracking(num - arr[idx], idx + 1);	break;
                    case 2:	backTracking(num * arr[idx], idx + 1);	break;
                    case 3:	backTracking(num / arr[idx], idx + 1);	break;
                }
                // 재귀호출이 종료되면 다시 해당 연산자 개수를 복구한다.
                op[i]++;
            }
        }
    }
}

 

[정리]

 

그림의 예가 약간 잘못된것같고 이상하지만 참고하고 보자. 그림을 예로 들어보자면 입력값이 첫째 줄에 3, 둘째 줄에 2 3 4 셋째 줄에 1 1 1 1을 입력받았을 경우의 대략적인 그림이다. 첫번째 숫자부터시작해 2 + 3 - 4 순서대로 계산을 하고 그 다음은 2 + 3 * 4, 그 다음은 2 + 3 / 4 순서대로 진행하게 된다. 숫자가 더 많거나 연산자가 더 많을 경우에도 그림과 같이 진행된다고 보면 된다.

 

[느낀점]

아직 하노이 알고리즘도 그렇고, dfs, bfs 등 어려운 알고리즘은 혼자만의 생각으로는 아직 풀 수 없는것같다. 다른 글을 참고해가며 정말 어려울 경우에는 클론코딩을 해가며 풀게된다. 중요한 점은 그냥 참고하고 끝나는게 아니라 문제를 풀 때 만큼은 그림으로 그려가며 완벽하게 이해하는게 중요한것 같다.

+ Recent posts