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

 

9663번: N-Queen

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

www.acmicpc.net

코드설명

브루트포스(BruteForce) + 시간초과(TimeOut) 문제입니다.

 

처음에는 완전탐색을 통해 진행했습니다만, 이 문제 같은경우 Queen의 위치를 고려해야하기에  조금 더 최적화 시킬 수 있습니다.

 

스택이 너무 쌓여 메모리초과가 나는 주 원인의 코드를 비교해봅시다.

 

먼저 스택이 쌓여 메모리초과가 나는 simulate 재귀함수입니다.

아래 코드를 보듯이, 저는 "모든"경우를 다 확인합니다. 이렇게 진행할경우, 퀸은 한 행에 놓으면 해당 행에는 처리할 수 없는데도, 한 행을 다시 한번 도므로 불필요합니다.

public static void simulate(int level, int r, int c, int cnt) {
    if(cnt == N) {
        answer += 1;
        return ;
    }
    if(c >= N) {
        r = r + 1;
        c = 0;
    }
    if(r >= N) {
        return ;
    }

    //근처에 퀸이 있는지 없는지 검사해야한다.
    if(arr[r][c] == false && checkQueen(r, c) == true) {
        arr[r][c] = true;
        simulate(level + 1, r, c + 1, cnt + 1);
        arr[r][c] = false;
    }
    simulate(level + 1, r, c + 1, cnt);

}

 

 

퀸의 움직임을 최적화한 코드입니다.

또한 문제를 읽어보면, NXN 개의 Map에서 퀸의 조건을 마주치면서 N개를 넣으려면 반드시 각 행마다 모든 Queen이 들어가야합니다. 그러므로 r==N 까지 도달한다면 N개를 모두 삽입한 것 입니다. 이러한 코드는 성공할경우 바로 r을 이동시키므로 문제가 없습니다.

public static void simulate(int r) {
    if(r == N) {
        answer++;
        return ;
    }

    for(int c = 0; c < N;c++) {
        if(checkQueen(r, c) == true) {
            arr[r][c] = true;
            simulate(r + 1);
            arr[r][c] = false;
        }
    }
}

 

또한 위의 재귀함수를 수정하는것 외에도 Queen의 위치를 확인하는 코드 또한 수정이 필요합니다.

 

먼저 시간초과를 유발하는 코드입니다.

public static boolean checkQueen(int r, int c) {
    //북쪽에서부터 시계방향으로.
    int[] dx = {-1, -1,-1};
    int[] dy = {-1, 0, 1};

    //3방향을 모두 확인합니다.
    for(int dir = 0; dir < 3; dir++) {
        int startR = r;
        int startC = c;
        while(true) {
            startR += dx[dir];
            startC += dy[dir];
            if(startR < 0 || startR >= N || startC < 0 || startC >= N) break; 
            if(arr[startR][startC] == true) {
                return false;
            }
        }
    }

    return true;
}

각 방향을 총 3번씩 모두 확인합니다.

 

하나의 반복문에서 3가지 방향을 모두 확인합니다.

public static boolean checkQueen(int r, int c) {
    // 북쪽, 북서쪽, 북동쪽 방향으로 퀸이 있는지 확인합니다.
    for(int i = 0; i < r; i++) {
        if(arr[i][c]) return false; // 같은 열에 퀸이 있는지 확인
        if(c - (r - i) >= 0 && arr[i][c - (r - i)]) return false; // 북서쪽 대각선에 퀸이 있는지 확인
        if(c + (r - i) < N && arr[i][c + (r - i)]) return false; // 북동쪽 대각선에 퀸이 있는지 확인
    }
    return true;
}

 

코드

시간초과 해결코드

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

public class Main {

	public static int N,M, K;
	public static boolean[][] arr;
	public static int answer =0;
    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());
    	arr = new boolean[N][N];

    	simulate(0);
    	System.out.println(answer);
    }

    
    public static void simulate(int r) {
    	if(r == N) {
    		answer++;
    		return ;
    	}
    	
    	for(int c = 0; c < N;c++) {
    		if(checkQueen(r, c) == true) {
    			arr[r][c] = true;
    			simulate(r + 1);
    			arr[r][c] = false;
    		}
    	}
    }
    public static boolean checkQueen(int r, int c) {
        // 북쪽, 북서쪽, 북동쪽 방향으로 퀸이 있는지 확인합니다.
        for(int i = 0; i < r; i++) {
            if(arr[i][c]) return false; // 같은 행에 퀸이 있는지 확인
            if(c - (r - i) >= 0 && arr[i][c - (r - i)]) return false; // 북서쪽 대각선에 퀸이 있는지 확인
            if(c + (r - i) < N && arr[i][c + (r - i)]) return false; // 북동쪽 대각선에 퀸이 있는지 확인
        }
        return true;
    }
    
}

브루트포스로 풀경우 시간초과 및 메모리초과가 나옵니다.

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

public class Main {

	public static int N,M, K;
	public static boolean[][] arr;
	public static int answer =0;
    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());
    	arr = new boolean[N][N];

    	simulate(0, 0, 0, 0);
    	System.out.println(answer);
    }

	//퀸은 상하좌우, 대각선까지 고려해야한다.
    public static void simulate(int level, int r, int c, int cnt) {
    	if(cnt == N) {
    		answer += 1;
    		return ;
    	}
    	if(c >= N) {
    		r = r + 1;
    		c = 0;
    	}
    	if(r >= N) {
    		return ;
    	}
    	
    	//근처에 퀸이 있는지 없는지 검사해야한다.
    	if(arr[r][c] == false && checkQueen(r, c) == true) {
    		arr[r][c] = true;
    		simulate(level + 1, r, c + 1, cnt + 1);
    		arr[r][c] = false;
    	}
    	simulate(level + 1, r, c + 1, cnt);
    	
    }
    
    public static boolean checkQueen(int r, int c) {
    	//북쪽에서부터 시계방향으로.
    	int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1};
    	int[] dy = {0, 1, 1, 1, 0, -1, -1, -1};

    	//8방향을 모두 확인합니다.
    	for(int dir = 0; dir < 8; dir++) {
    		int startR = r;
    		int startC = c;
    		while(true) {
    			startR += dx[dir];
    			startC += dy[dir];
    			if(startR < 0 || startR >= N || startC < 0 || startC >= N) break; 
    			if(arr[startR][startC] == true) {
    				return false;
    			}
    		}
    	}
    	
    	return true;
    }
    
}

 

 

다른 코드를 참고해보았습니다. 가장 효율적인 속도의 코드입니다.

package Main;

import java.io.IOException;
import java.util.Scanner;


public class Main {
	
	//같은열 기준 y를 기준
	static boolean isused1[] = new boolean[40];
	//우측상단대각선기준 x+y ==
	static boolean isused2[] = new boolean[40];
	//우측하단대각선기준 x-y 인데 음수를 피하기위해 x-y+N-1 로 진행
	static boolean isused3[] = new boolean[40];
	static int N,cnt=0;
	
	public static void main(String[] args) throws IOException{
		Scanner sc = new Scanner(System.in);
		N=sc.nextInt();
		
		func(0);
		System.out.println(cnt);
	}
	
	public static void func(int cur) {
		if(cur == N) {
			cnt ++;
			return ;
		}
		
		
		//cur이 x임, i가 y임
		//이유는 한행씩 채워가면서 진행하기에 cur : 0 은 0번째 행을 채우자는 의미임
		for(int i=0;i<N;i++) {
			if(isused1[i] || isused2[cur+i] || isused3[cur-i+N-1]) {
				continue;
			}
			isused1[i] = true;
			isused2[cur+i] = true;
			isused3[cur-i+N-1] = true;
			func(cur+1);
			isused1[i] = false;
			isused2[cur+i] = false;
			isused3[cur-i+N-1] = false;
			
		}
	}


	
}

+ Recent posts