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

 

1799번: 비숍

첫째 줄에 체스판의 크기가 주어진다. 체스판의 크기는 10이하의 자연수이다. 둘째 줄부터 아래의 예와 같이 체스판의 각 칸에 비숍을 놓을 수 있는지 없는지에 대한 정보가 체스판 한 줄 단위로

www.acmicpc.net

코드설명

백트래킹 문제입니다.

 

문제를 처음봤을시 전체 체스판을 순회하면서 로직을 수행하면 될 것이라는 생각으로 코드를 작성했습니다.

문제에서 유의해야할점은

1.비숍을 둘 수 있더라도 비숍을 두지 않고 진행할때도 있어야합니다. 그렇게해야만, 완전탐색이 진행됩니다. 만약 비숍을 두지못할경우에만 진행을 하게된다면, 모든 경우를 탐색하지 못합니다.

if(arr[nowX][nowY] == 1 && visited[nowX][nowY] == false) {
//원상복구를 위한 메모리배열 사용시 메모리 초과.
BisshopVisited[nowX][nowY] = true; //실제로 비숍이 존재하는 위치 확인
setBisshop(nowX, nowY); //비숍을 두었을때 대각선 위치들을 모두 visited[][] true 로 설정해야합니다.
simulate(nowX, nowY + 1, cnt + 1);
BisshopVisited[nowX][nowY] = false;
calculateBisshop(nowX, nowY);
}
simulate(nowX, nowY + 1, cnt); //만약 비숍을 두지못한경우만 다음 칸으로 넘어가는것이 아닌 비숍을 둘 수 있더라도 두지않고 다음칸으로 이동합니다.

 

 

하지만, 이렇게 할경우 시간초과에 걸리게됩니다. ( 관련 코드 하단에 위치 )

시간초과가 나는 이유는, O( 2 ^ (N * N) ) 비숍을 놓을것인지 놓지않을것인지 총 N^N번을 고민하므로 시간복잡도가 이렇습 니다. 체스의 킈는 O(2 ^ ( 10 * 10 )) 는 O( 2^ 100) 입니다. 10억을 넘어가므로 시간초과가 납니다.

 

시간초과를 해결하기 위해서는 다른 방식으로 해결이 필요합니다.

비숍은 대각선으로만 움직일 수 있습니다. 즉, 체크무늬모양의 타일이 존재한다고할때 (0,0)을 하얀색 타일이라고한다면, (0,1) 은 검은색 타일이라고 할 수 잇습니다.

이를 활용해 각 하얀타일을 최대한 많이 선택하는경우와 검은타일을 최대한 많이 선택하는경우로 나누어서 구합니다.

그렇다면 시간복잡도는 O( 2 ^ ( N/2 * N/2)  + 2 ^ (N/2 * N/2))  입니다. 시간복잡도가 훨씬 줄어드므로 성공합니다.

코드

비숍을 하얀칸과 검은칸으로 나눈경우 시간복잡도는 O( 2 ^ ( N/2 * N/2)  + 2 ^ (N/2 * N/2))  

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static int N;
public static int[][] arr;
public static boolean[][] visited;
public static boolean[][] BisshopVisited;
//좌상, 우상, 우하, 좌하
public static int[] dx = {-1,-1,1,1};
public static int[] dy = {-1,1,1,-1};
public static int answer = 0;
public static int whiteCnt = 0, blackCnt = 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 int[N][N];
BisshopVisited = 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());
}
}
whiteSearchSimulate(0, 0, 0, 0);
blackSearchSimulate(0, 0, 1, 0);
System.out.println(whiteCnt + blackCnt);
}
public static void whiteSearchSimulate(int level, int r, int c, int cnt) {
if(c >= N) {
r = r + 1;
if( r % 2 == 0) {
c = 0;
} else {
c = 1;
}
}
// if(level >= N * N) {
// return ;
// }
if(r >= N) {
whiteCnt = Math.max(whiteCnt, cnt); //종료시점에검사.
return ;
}
if(arr[r][c] == 1 && checkAvailable(r, c) == true) {
BisshopVisited[r][c] = true;
whiteSearchSimulate(level + 1, r, c + 2, cnt + 1);
BisshopVisited[r][c] = false;
}
whiteSearchSimulate(level + 1, r, c + 2, cnt);
}
public static void blackSearchSimulate(int level, int r, int c, int cnt) {
if(c >= N) {
r = r + 1;
if( r % 2 == 0) {
c = 1;
} else {
c = 0;
}
}
// if(level >= N * N) {
// return ;
// }
if(r >= N) {
blackCnt = Math.max(blackCnt, cnt); //종료시점에검사.
return ;
}
if(arr[r][c] == 1 && checkAvailable(r, c) == true) {
BisshopVisited[r][c] = true;
blackSearchSimulate(level + 1, r, c + 2, cnt + 1);
BisshopVisited[r][c] = false;
}
blackSearchSimulate(level + 1, r, c + 2, cnt);
}
public static boolean checkAvailable(int r, int c) {
for(int dir = 0; dir < 4; dir ++) {
int nr = r + dx[dir];
int nc = c + dy[dir];
while(true) {
if(nr < 0 || nr >= N || nc < 0 || nc >= N) break;
if(BisshopVisited[nr][nc] == true) return false; //해당 대각선을 확인했을떄 이미 다른 비숍이 존재한다면 놓지못합니다.
nr = nr + dx[dir];
nc = nc + dy[dir];
}
}
return true;
}
}

 

방문처리를 간단하게 바꿔본 코드지만 시간초과가 여전함 시간복잡도는 O( 2 ^ (N * N) ) 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static int N;
public static int[][] arr;
public static boolean[][] visited;
public static boolean[][] BisshopVisited;
//좌상, 우상, 우하, 좌하
public static int[] dx = {-1,-1,1,1};
public static int[] dy = {-1,1,1,-1};
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 int[N][N];
BisshopVisited = 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());
}
}
simulate(0, 0, 0);
System.out.println(answer);
}
public static void simulate(int nowX, int nowY, int cnt) {
if(nowX >= N-1 && nowY >= N) {
answer = Math.max(answer, cnt);
return ;
}
if(nowY >= N) {
nowX += 1;
nowY = 0;
}
if(arr[nowX][nowY] == 1 && checkAvailable(nowX, nowY) == true) {
BisshopVisited[nowX][nowY] = true; //실제로 비숍이 존재하는 위치 확인
simulate(nowX, nowY + 1, cnt + 1);
BisshopVisited[nowX][nowY] = false;
}
simulate(nowX, nowY + 1, cnt);
}
public static boolean checkAvailable(int startX, int startY) {
for(int dir=0;dir<4;dir++) {
int nx = startX + dx[dir];
int ny = startY + dy[dir];
while(true) {
if(nx < 0 || nx >= N || ny < 0 || ny >= N) break;
if(BisshopVisited[nx][ny] == true) return false;
nx += dx[dir];
ny += dy[dir];
}
}
return true;
}
}

 

 

시간초과가 나는 코드 시간복잡도는 O( 2 ^ (N * N) ) 

10
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
public static int N;
public static int[][] arr;
public static boolean[][] visited;
public static boolean[][] BisshopVisited;
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 int[N][N];
visited = new boolean[N][N];
BisshopVisited = 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());
}
}
simulate(0, 0, 0);
System.out.println(answer);
}
public static void simulate(int nowX, int nowY, int cnt) {
if(nowX >= N-1 && nowY >= N) {
answer = Math.max(answer, cnt);
return ;
}
if(nowY >= N) {
nowX += 1;
nowY = 0;
}
if(arr[nowX][nowY] == 1 && visited[nowX][nowY] == false) {
//원상복구를 위한 메모리배열 사용시 메모리 초과.
BisshopVisited[nowX][nowY] = true; //실제로 비숍이 존재하는 위치 확인
setBisshop(nowX, nowY); //비숍을 두었을때 대각선 위치들을 모두 visited[][] true 로 설정해야합니다.
simulate(nowX, nowY + 1, cnt + 1);
BisshopVisited[nowX][nowY] = false;
calculateBisshop(nowX, nowY);
}
simulate(nowX, nowY + 1, cnt);
}
public static void setBisshop(int startx, int starty) {
int originStartX = startx;
int originStartY = starty;
visited[startx][starty] = true;
//우상 대각선 처리
while(true) {
int nx = startx - 1;
int ny = starty + 1;
if( nx < 0 || nx >= N || ny < 0 || ny >= N) break;
if( arr[nx][ny] == 1 ) {
visited[nx][ny] = true;
}
startx -= 1;
starty += 1;
}
startx = originStartX;
starty = originStartY;
//좌하 대각선 처리
while(true) {
int nx = startx + 1;
int ny = starty - 1;
if( nx < 0 || nx >= N || ny < 0 || ny >= N) break;
if( arr[nx][ny] == 1 ) {
visited[nx][ny] = true;
}
startx += 1;
starty -= 1;
}
startx = originStartX;
starty = originStartY;
//좌상 대각선 처리
while(true) {
int nx = startx - 1;
int ny = starty - 1;
if( nx < 0 || nx >= N || ny < 0 || ny >= N) break;
if( arr[nx][ny] == 1 ) {
visited[nx][ny] = true;
}
startx -= 1;
starty -= 1;
}
startx = originStartX;
starty = originStartY;
//우하 대각선 처리
while(true) {
int nx = startx + 1;
int ny = starty + 1;
if( nx < 0 || nx >= N || ny < 0 || ny >= N) break;
if( arr[nx][ny] == 1 ) {
visited[nx][ny] = true;
}
startx += 1;
starty += 1;
}
}
public static void calculateBisshop(int startx, int starty) {
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
visited[i][j] = false;
}
}
for(int i=0;i<N;i++) {
for(int j=0;j<N;j++) {
if(BisshopVisited[i][j] == true) {
setBisshop(i, j);
}
}
}
}
}

+ Recent posts