Algorithm/백준

[백준] 골드3 15684

sangyunpark 2023. 9. 12. 21:27

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

 

15684번: 사다리 조작

사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선

www.acmicpc.net

 

완전탐색으로 구현을 하려 했지만...

시간복잡도를 계산해보니 시간초과가 날 것 같아서, 백트래킹을 사용해주었다.

 

흐름

임의로 사다리를 놓고, 사다리를 놓은 후 정답 조건을 만족하는지 확인하는 것이다.

 

주의할 점

사다리는 이어질 수 없다는 점

for (int i = here; i <= h; i++) {
            for (int j = 1; j <= n; j++){
                if(visited[i][j] != 0 || visited[i][j-1] != 0 || visited[i][j+1] != 0) continue;
                visited[i][j] = 1;
                go(i, cnt+1);
                visited[i][j] = 0; // 방문했던 곳 취소하기
            }
        }

 

중요한 곳

백트래킹을 위한 한정 조건

if(cnt > 3 || ret <= cnt){ // 이미 3개가 넘은 경우
  return;
}

이미 3개가 넘은 경우는 당연하거니와 정답을 만족하는 ret가 cnt보다 작거나 같은 경우는 더 탐색해줄 필요가 없다.

왜? 최솟값을 찾아야 하기 때문에, 가지를 쳐버리는 것이다!

 

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

public class Main {

    public static int n;
    public static int m;
    public static int h;

    public static int[][] visited;

    public static int ret = Integer.MAX_VALUE;
    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());
        m = Integer.parseInt(st.nextToken());
        h = Integer.parseInt(st.nextToken());

        visited = new int[34][34]; //

        for (int i = 0; i < m; i++) { // 사다리 탐지
            st = new StringTokenizer(br.readLine());

            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            visited[a][b] = 1; // indexing
        }

        go(1,0);

        System.out.println(ret == Integer.MAX_VALUE ? -1 : ret);

    }

    public static void go(int here, int cnt){
        if(cnt > 3 || ret <= cnt){ // 이미 3개가 넘은 경우
            return;
        }

        if(check()){ // 어라랏 도착 완료!
            ret = Math.min(ret,cnt);
            return;
        }

        for (int i = here; i <= h; i++) {
            for (int j = 1; j <= n; j++){
                if(visited[i][j] != 0 || visited[i][j-1] != 0 || visited[i][j+1] != 0) continue;
                visited[i][j] = 1;
                go(i, cnt+1);
                visited[i][j] = 0; // 방문했던 곳 취소하기
            }
        }

    }

    public static boolean check(){ // 시작 사다리와 도착 사다리가 같은 경우
        for (int i = 1; i <= n; i++) {
            int start = i;
            for (int j = 1; j <= h; j++) {
                if(visited[j][start] == 1){
                    start++;
                }else if(visited[j][start-1] == 1){
                    start--;
                }
            }
            if(start != i){ // 쭉 다 돌았는데도 일치하지 않는 경우
                return false;
            }
        }
        return true;
    }
}