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

 

15685번: 드래곤 커브

첫째 줄에 드래곤 커브의 개수 N(1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 드래곤 커브의 정보가 주어진다. 드래곤 커브의 정보는 네 정수 x, y, d, g로 이루어져 있다. x와 y는 드래곤 커�

www.acmicpc.net

문제

구현 문제이다. 

 

해결 방안

map의 전체 크기는 101 x 101이 될 것이다. (전체 격자가 100x100 이므로)

1. 드래곤 커브의 갯수 N만큼 드래곤 커브의 정보들(x, y, d, g)을 받게 되는데

(drawDragonCurve() 함수 참고)
1.1. 각 드래곤 커브마다 g(세대)까지 map에 그려줘야한다.(드래곤 커브가 가지고 있는 꼭지점을 true로 만들어줘야한다. map[x][y] = true).
1.2. 이때 드래곤 커브를 그리는데 규칙이 있는데 다음 세대를 갈때 전 세대의 (가장 늦게 들어온 것부터 첫 번째 까지) 영향을 받는 것이다. 이때 반시계방향으로 방향이 바뀐다.
1.3. 따라서 드래곤 커브를 그리기 위해 해당 방향들을 list에 다 기억하고 있다가 주어진 x,y에 list 만큼 이어서 그려준다.
1.4. 드래곤 커브 갯수 N 만큼 드래곤 커브를 map에 그려준다. 

2. map에 드래곤 커브를 다 그리고 나면 1x1 정사각형의 네 꼭지점이 전부 true인 경우의 갯수를 찾으면 된다.

 

코드

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

// 드래곤 커브
// 세가지 속성(시작점, 시작방향, 세대)
// 이차원 좌표 평면 위에서 정의됨(x축은 동 방향 y축은 남 방향)

// 첫째 줄에 크기가 1x1인 정사각형의 네 꼭짓점이 모두 드래곤 커브의 일부인 것의 갯수를 출력한다.
public class Main {
	
	// 전체 격자 100 x 100
	static int N; // 드래곤 커브 갯수 (1~20)
	static int x, y; // 드래곤 커브의 시작점(x,y) (0~100)
	static int d; // 시작 방향 (0~3) (0:동(x+)  1:북(y-)  2:서(x-)  3:남(y+))
	static int g; // 세대 (0~10)
	static boolean[][] map = new boolean[101][101]; // 드래곤 커브가 들어가면 해당 꼭지점(y, x)는 true
	
	static List<Integer> directions; // 선들의 방향을 저장할 List
	
	// (0:동(x+)  1:북(y-)  2:서(x-)  3:남(y+))
	static int[] dx = {1, 0, -1, 0};
	static int[] dy = {0, -1, 0, 1};
	
	public static void main(String[] args) throws NumberFormatException, IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(br.readLine());
		
		String str;
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			x = Integer.parseInt(st.nextToken());
			y = Integer.parseInt(st.nextToken());
			d = Integer.parseInt(st.nextToken());
			g = Integer.parseInt(st.nextToken());
			
			drawDragonCurve();
		}
		// === 입력 및 맵에 드래곤 커브 적용====
		
		int result = getSquareCnt();
		System.out.println(result);
	}
	
	// 드래곤 커브 그리기
	static void drawDragonCurve() {
		// 초기화
		directions = new ArrayList<>();
		directions.add(d); // 처음 방향 넣기

		// 드래곤 커브가 그려지는 선 방향 저장하기
		int dir;
		//g 세대 까지 반복
		while(g-- > 0) {
			// 전 세대의 영향을 받으며 stack과 같이 쌓여지므로 뒤에서부터 get
			for(int i=directions.size()-1; i>=0; i--) {
				dir = (directions.get(i)+1) % 4;
				directions.add(dir);
			}
		}
		
		// map에서 드래곤 커브가 지나는 꼭지점 그리기 
		int nx, ny;
		int cx = x;
		int cy = y;
		map[x][y] = true;
		for(int i=0; i<directions.size(); i++) {
			dir = directions.get(i);
			
			nx = cx + dx[dir];
			ny = cy + dy[dir];
			
			map[nx][ny] = true;
			
			// 계속 이어서 그려가는 것이므로 cx, cy를 바꿔줘야함
			cx = nx;
			cy = ny;
		}
	
	}
	
	//크기가 1x1인 정사각형의 네 꼭짓점이 모두 드래곤 커브의 일부인 것의 갯수를 구하는 함수
	static int getSquareCnt() {
		int cnt = 0;
		
		for(int i=0; i<100; i++) { //y축
			for(int j=0; j<100; j++) { //x축
				if(map[i][j] && map[i+1][j] && map[i][j+1] && map[i+1][j+1])
					cnt++;
			}
		}
		
		return cnt;
	}

}

 

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

 

15683번: 감시

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감��

www.acmicpc.net

 

문제

DFS + 구현(Simultation) 문제이다.

해결 방안

CCTV 클래스를 만들어서 각 CCTV의 y(row), x(col), kinds(cctv 종류), dir[](해당 cctv가 가진 방향) 정보를 가지고 있도록 하였다.

1. Main에서 사무실 공간 값(map[i][j])을 입력 받을 때 cctv 값(1~5)가 포함되면 List<CCTV> cctvs에 넣어주었다.

2. cctvs 갯수(List 사이즈)만큼 DFS 탐색을 한다. 이때 DFS 탐색을 하는 목적은 List에 있는 CCTV들의 모든 방향을 설정해주기 위함이다.
이때 1,3,4 CCTV 같은 경우 4방향이 나올 수 있으므로 4가지 경우(90도씩 회전한 경우)를 DFS탐색을 해주고 2 CCTV 같은경우 2가지(상하, 좌우)의 경우를 DFS 탐색, 5 CCTV 같은 경우 1가지 경우를 DFS 탐색해준다.

3. CCTV 갯수만큼 DFS 탐색이 도달(기저사례)하게 되면 각 CCTV들이 감시하는 방향으로 copyMap을 재설정해준다. (문제의 예시에서는 #을 이용하였으나 여기서는 7값으로 해주었다.)

4. 그 후 copyMap에서 사각지대(0인 값) 인 부분을 세서 최소인 경우를 구해준다. 

 

코드

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

// 감시
// NxM 직사각형 사무실
// 총 k개의 cctv
// cctv 종류 5개 
// cctv는 감시할 수 있는 방향에 있는 칸 전체를 감시할 수 있음
// 사무실에 벽이 있는데 cctv는 벽을 통과할 수 없다.
// cctv가 감시할 수 없는 영역은 사각지대
// cctv는 회전 시킬 수 있음(회전은 항상 90도 방향)
// 0은 빈칸 6은 벽 1~5는 cctv 번호

// 사각 지대의 최소 크기 출력
public class Main {
	
	static class CCTV{
		int y; // 세로
		int x; // 가로
		
		int kinds; // cctv 종류 (1~5)
		int[] dir; // 방향 (0~3)
		
		CCTV(int y, int x, int kinds){
			this.y = y;
			this.x = x;
			this.kinds = kinds;
			
			if(kinds==1) {
				this.dir = new int[1];
				dir[0] = 0;
			}
			else if(kinds==2) {
				this.dir = new int[2];
				dir[0] = 0;
				dir[1] = 2;
			}
			else if(kinds==3) {
				this.dir = new int[2];
				dir[0] = 0;
				dir[1] = 3;
			}
			else if(kinds==4) {
				this.dir = new int[3];
				dir[0] = 0;
				dir[1] = 2;
				dir[2] = 3;
			}
			else if(kinds==5) {
				this.dir = new int[4];
				dir[0] = 0;
				dir[1] = 1;
				dir[2] = 2;
				dir[3] = 3;
			}
		}
	}
	static int N; // 세로 (1~8)
	static int M; // 가로 (1~8)
	static int[][] map; // 사무실
	static int k; // cctv 갯수
	
	static List<CCTV> cctvs = new ArrayList<>();
	// 방향은 동 남 서 북
	static int[] dy = {0, 1, 0, -1}; // 세로
	static int[] dx = {1, 0, -1, 0}; // 가로
	
	static int result = Integer.MAX_VALUE; // 사각 지대의 최소 크기
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str;
		str = br.readLine();
		
		N = Integer.parseInt(str.split(" ")[0]);
		M = Integer.parseInt(str.split(" ")[1]);
		
		map = new int[N][M];
		
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			for(int j=0; j<M; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
				
				// cctv 위치, 종류 List에 삽입
				if(map[i][j] >= 1 && map[i][j] <=5) {
					cctvs.add(new CCTV(i, j, map[i][j]));
				}
			}
		}
		k = cctvs.size();
		// ======= 입력 끝 ========
		dfs(0);
		
		System.out.println(result);
	}
	
	// y,x가 지도 범위 벗어나는지 확인
	static boolean isMapRange(int y, int x) {
		boolean flag = true;
		
		if(y<0 || y>=N || x<0|| x>=M)
			return false;
		
		return flag;
	}
	
	// copyMap에서 cctv가 감시중인 곳을 7로 바꿔줌
	static int[][] setCCTV(int[][] map){
		
		CCTV cctv;
		int cy, cx; // 세로, 가로
		int ny, nx;
		for(int i=0; i<cctvs.size(); i++) {
			cctv = cctvs.get(i);
			
			cy = cctv.y;
			cx = cctv.x;
			
			// i번째 cctv의 방향들로 감시하는 구역 값(7)로 바꿔줌
			int dir;
			for(int j=0; j<cctv.dir.length; j++) {
				dir = cctv.dir[j];// 해당 방향
				
				ny = cy;
				nx = cx;
				// j번째 방향을 7로 바꿔줌
				while(true) {
					ny += dy[dir];
					nx += dx[dir];
					
					// ny, nx 범위 확인(사무실내인지)
					if(isMapRange(ny, nx)) {
						// 빈 공간이면 감시 공간으로 바꿔줌
						if(map[ny][nx] == 0)
							map[ny][nx] = 7;
						// 벽에 도달하면 멈춤
						else if(map[ny][nx] == 6)
							break;
					}
					// 사무실 범위 벗어나면 멈춤
					else {
						break;
					}
					
				}
			}
		}
		
		return map;
	}
	
	// CCTV들 방향설정 및 감시하고 있는 영역을 구하는 함수
	static void dfs(int depth) {
		
		if(depth >= k) {
			
			// copyMap 복사
			int[][] copyMap = new int[N][M];
			for(int i=0; i<N; i++) {
				for(int j=0; j<M; j++) {
					copyMap[i][j] = map[i][j];
				}
			}
			
			// 정해진 방향으로 copyMap 재설정(cctv 감시중인 곳 #대신 7로)
			copyMap = setCCTV(copyMap);
			
			// 사각지대 갯수 구하기(0인 곳)
			int sum = 0;
			for(int i=0; i<N; i++) {
				for(int j=0; j<M; j++) {
					//System.out.print(copyMap[i][j]);
					if(copyMap[i][j] == 0)
						sum++;
				}
				//System.out.println();
			}
			//System.out.println();
			// 최소 사각지대 갯수 구하기
			result = Math.min(result, sum);
		}
		else {
			
			// 각 cctv 방향 설정
			CCTV cctv;
			cctv = cctvs.get(depth);
			
			int[] temp = cctv.dir.clone();
			// 1,3,4번 cctv인 경우 4방향
			if(cctv.kinds==1 || cctv.kinds==3 || cctv.kinds==4) {
				for(int j=0; j<4; j++) {
					
					for(int k=0; k<cctv.dir.length; k++) {
						cctv.dir[k] += j;
						cctv.dir[k] = cctv.dir[k] % 4;
					}
					dfs(depth+1);
					// dir 원래대로 돌려주기
					cctv.dir = temp.clone();
				}
			}
			// 2번 cctv인 경우 2방향 가능 (상하, 좌우)
			else if(cctv.kinds==2) {
				for(int j=0; j<2; j++) {
					for(int k=0; k<cctv.dir.length; k++) {
						cctv.dir[k] += j;
						cctv.dir[k] = cctv.dir[k] % 4;
					}
					dfs(depth+1);
					// dir 원래대로 돌려주기
					cctv.dir = temp.clone();
				}
			}
			// 5번 cctv인 경우 1방향
			else if(cctv.kinds==5) {
				dfs(depth+1);
			}
			
		}
		
	}

}

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

 

14891번: 톱니바퀴

첫째 줄에 1번 톱니바퀴의 상태, 둘째 줄에 2번 톱니바퀴의 상태, 셋째 줄에 3번 톱니바퀴의 상태, 넷째 줄에 4번 톱니바퀴의 상태가 주어진다. 상태는 8개의 정수로 이루어져 있고, 12시방향부터 �

www.acmicpc.net

문제

구현(Simulation) 문제이다.

 

해결 방안

문제 구성을 크게 1. 입력 받는 부분, 2. 각 톱니바퀴 회전 방향 입력에 따른 전체 톱니바퀴 회전 방향 구하기, 3. 회전 방향에 따른 각 톱니바퀴 상태 재설정, 4. 다 회전한 후 톱니바퀴 점수 합 구하기 로 4가지로 구성하였다.

1. 입력 받는 부분은 그냥 입력 받으면 되는 부분이라 넘어갑니다.

2. 각 톱니 바퀴 회전 방향 입력에 따른 전체 톱니바퀴 회전 방향 구하는 것은 톱니바퀴가 4개로 설정되어 있어 전체를 직접 구현하였다.
(톱니바퀴 1이 회전할때, 톱니바퀴 2가 회전할때, 톱니바퀴 3이 회전할때, 톱니바퀴 4가 회전할 때)
각각 톱니바퀴 회전 방향이 주어졌을 때 위의 4가지 경우를 모두 구해주었다.
그리고 static 으로 int형 gearDir[]에 각 톱니바퀴들이 회전해야 하는 방향을 넣어주었다. (1:시계, 0:정지, -1:반시계) 

3. 회전 방향에 따른 각 톱니바퀴 상태 재설정하는 부분은 위에서 저장한 gearDir[]를 참고하여 각각의 톱니바퀴들의 상태를 재설정해주었다.
gearDir[gearIdx]가 0이면 해당 톱니바퀴는 정지되야 하므로 넘어가주고 1과 -1일때 각각의 상태를 gear[][]에 재설정해주었다.

4. gear[][]를 가지고 문제의 출력에 맞게 점수를 구해준다.

 

코드

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

// 톱니바퀴
// 총 8개의 톱니를 가지고 있는 톱니바퀴 4개가 일렬로 놓여져 있다.
// 톱니는 N극 또는 S극 중 하나
// 톱니바퀴가 다른 톱니바퀴와 서로 맞닿는 톱니의 극이 다르면 서로 반대방향으로 회전하게 됨

public class Main {
	
	// 톱니바퀴 4개의 톱니 상태(12시 방향부터 시작)(N극은 0, S극은 1)
	static int[][] gear = new int[5][9]; //[1~4][1~8] 
	static int K; // 회전 횟수(1~100)
	static int gearIdx; // 회전할 톱니(1~4)
	static int dir; // 입력 받은 값
	static int[] gearDir = new int[5]; // 각 톱니바퀴들의 회전 방향 (시계방향: 1, 반시계방향:-1 +  정지:0)
	static int result; // 네 톱니바튀의 점수의 합

	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		String str;
		for(int i=1; i<=4; i++) {
			str = br.readLine();
			for(int j=1; j<=8; j++) {
				gear[i][j] = Integer.parseInt(str.split("")[j-1]);
			}
		}
		
		K = Integer.parseInt(br.readLine());
		StringTokenizer st;
		for(int i=0; i<K; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			
			gearIdx = Integer.parseInt(st.nextToken());
			dir = Integer.parseInt(st.nextToken());
			
			caculateGearDir();
			rotateGear();
		}
		
		result = 0;
		caculateScore();
		System.out.println(result);
		
	}
	
	// 입력 받은 gearIdx, dir를 이용한 다른 기어 회전 방향 구하기
	static void caculateGearDir(){
		// 초기화
		for(int i=1; i<=4; i++) {
			gearDir[i] = 0;
		}
		
		// 해당 gear방향 입력받은 dir 값으로 설정
		gearDir[gearIdx] = dir;

		// 다른 톱니바퀴와 만나는 부분 3, 7
		switch(gearIdx) {
		
			// 톱니바퀴 1이 회전할 경우 1->2->3->4
			case 1:
				// 톱니바퀴 1이 회전하고 맞물린 부분이 값이 다를때 톱니바퀴2 반대방향으로 회전
				if(gearDir[1] != 0 && gear[1][3] != gear[2][7] )
					gearDir[2] = gearDir[1] * -1;

				// 톱니 2와 톱니 3
				if(gearDir[2] != 0 && gear[2][3] != gear[3][7] )
					gearDir[3] = gearDir[2] * -1;
				
				// 톱니 3과 톱니 4
				if(gearDir[3] != 0 && gear[3][3] != gear[4][7] )
					gearDir[4] = gearDir[3] * -1;
					
				break;
			
			// 톱니 바퀴 2가 회전하는 경우 2->3->4 2->1
			case 2:
				// 톱니 2와 1
				if(gearDir[2] != 0 && gear[2][7] != gear[1][3])
					gearDir[1] = gearDir[2] * -1;
				
				// 톱니 2와 3
				if(gearDir[2] != 0 && gear[2][3] != gear[3][7])
					gearDir[3] = gearDir[2] * -1;
				
				// 톱니 3과 4
				if(gearDir[3] != 0 && gear[3][3] != gear[4][7])
					gearDir[4] = gearDir[3] * -1;
				
				break;
			
			// 톱니 바퀴 3이 회전하는 경우 3->4 3->2->1
			case 3:
				// 톱니 3과 4
				if(gearDir[3] != 0 && gear[3][3] != gear[4][7])
					gearDir[4] = gearDir[3] * -1;
				
				// 톱니 3과 2
				if(gearDir[3] != 0 && gear[3][7] != gear[2][3])
					gearDir[2] = gearDir[3] * -1;
				
				// 톱니 2와 1
				if(gearDir[2] != 0 && gear[2][7] != gear[1][3])
					gearDir[1] = gearDir[2] * -1;
				
				break;
			
			// 톱니 바퀴 4가 회전하는 경우 4->3->2->1
			case 4:
				// 톱니 4와 3
				if(gearDir[4] != 0 && gear[4][7] != gear[3][3])
					gearDir[3] = gearDir[4] * -1;
				
				// 톱니 3과 2
				if(gearDir[3] != 0 && gear[3][7] != gear[2][3])
					gearDir[2] = gearDir[3] * -1;
				
				// 톱니 2와 1
				if(gearDir[2] != 0 && gear[2][7] != gear[1][3])
					gearDir[1] = gearDir[2] * -1;
				
				break;
		}
			
	}
	
	// 각 톱니바퀴 각 gearDir에 맞게 회전
	static void rotateGear() {
		int[] tempGear;
		
		// 모든 톱니바퀴 gearDir에 맞게 회전시켜줌
		for(int i=1; i<=4; i++) {
			
			tempGear = new int[9];
			
			// 해당 톱니바퀴가 회전 안할 때(0)는 넘어감
			if(gearDir[i] == 0)
				continue;
			
			// 해당 톱니바퀴가 시계방향(1)
			if(gearDir[i] == 1) {
				tempGear[1] = gear[i][8];
				for(int j=2; j<=8; j++) {
					tempGear[j] = gear[i][j-1];
				}
			}
			// 해당 톱니바퀴가 반시계방향(-1)
			else if(gearDir[i] == -1) {
				for(int j=1; j<=7; j++) {
					tempGear[j] = gear[i][j+1];
				}
				tempGear[8] = gear[i][1];
			}
			
			// 회전한 배열을 gear에 다시 저장해줌
			gear[i] = tempGear.clone();
		}
	}
	
	// K번 회전시킨 이후에 네 톱니바퀴 점수의 합 출력
	//1번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 1점
	//2번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 2점
	//3번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 4점
	//4번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 8점
	static void caculateScore() {
		
		if(gear[1][1] == 1)
			result += 1;
		if(gear[2][1] == 1)
			result += 2;
		if(gear[3][1] == 1)
			result += 4;
		if(gear[4][1] == 1)
			result += 8;
	}
}

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

 

14890번: 경사로

첫째 줄에 N (2 ≤ N ≤ 100)과 L (1 ≤ L ≤ N)이 주어진다. 둘째 줄부터 N개의 줄에 지도가 주어진다. 각 칸의 높이는 10보다 작거나 같은 자연수이다.

www.acmicpc.net

문제

Simulation 문제이다. Queue를 사용하여 구현하다가 올라가는 경사에서 막혀서 다른사람 풀이를 참고하였다.

 

해결 방안

주어진 조건에 맞도록 구현하는 Simulation 문제이다.

1. 각 행과 열마다 지나길 수 있는 길인지 체크해서 갯수를 세준다.
2. 해당 행과 열을 1차원 배열로 만들어서 체크하기 쉽게 만든다. (checkPath()함수)
조건이 맞지 않는 경우 false를 리턴해준다.

 

코드

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

// 경사로
public class Main {

	static int N; // 지도 크기 NxN (2~100)
	static int L; // 경사로 길이 (1~N)
	static int[][] map; // 지도
	static boolean[][] visited; // 경사로 놓은지 확인
	static int pathCnt;
	
	public static void main(String[] args) throws IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str;
		str = br.readLine();
		N = Integer.parseInt(str.split(" ")[0]);
		L = Integer.parseInt(str.split(" ")[1]);
		pathCnt = 0;
		
		map = new int[N][N];
		visited = new boolean[N][N];
		
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			
			for(int j=0; j<N; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		// ===== 입력 끝 =====
		
		// 가능한 길인지 아닌지 체크
		for(int i=0; i<N; i++) {
			if(checkPath(i, 0, true)) // 행
				pathCnt++;
			
			if(checkPath(0, i, false)) // 열
				pathCnt++;
		}
		
		System.out.println(pathCnt);
	}

	// flag: true 행 검사
	// flag: false 열 검사
	static boolean checkPath(int x, int y, boolean flag) {
		
		int[] height = new int[N];
		boolean[] visited = new boolean[N];
		
		for(int i=0; i<N; i++) {
			if(flag)
				height[i] = map[x][i];
			else
				height[i] = map[i][y];
		}
		
		for(int i=0; i<N-1; i++) {
			// 높이가 같을 때
			if(height[i] == height[i+1]) {
				continue;
			}
			// 내려가는 경사
			else if(height[i]-height[i+1] == 1) {
				
				for(int j=i+1; j<=i+L; j++) {
					// 범위 넘어가거나 칸의 높이가 다르거나 이미 경사로가 있는 경우
					if(j>=N || height[i+1]!=height[j] || visited[j]) return false;
					visited[j] = true;
				}
			}
			// 올라가는 경사
			else if(height[i]-height[i+1] == -1) {
				
				for(int j=i; j>i-L; j--) {
					// 범위 넘어가거나 칸의 높이가 다르거나 이미 경사로가 있는 경우
					if(j<0 || height[i]!=height[j] || visited[j]) return false;
					visited[j] = true;
				}
			}
			// 높이가 2칸 이상 차이 날때 (길x)
			else {
				return false;
			}
		}
		
		return true;
	}
	
}

 

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

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

문제

DFS + 구현 으로 문제를 해결하였다.

 

해결 방안

전체적으로 보면 1. 스타트 팀과 링크 팀 반으로 사람을 나누는 모든 경우의 수와 2. 나눠진 팀의 점수 차이를 구하는 두가지를 구현해야한다.

1. 스타트 팀과 링크 팀 반으로 사람을 나누는 모든 경우의 수는 DFS 탐색을 이용하여 구현하였다.
기저사례가 N/2 일 때까지 DFS 탐색을 하며 N/2 개의 visited를 true로 만들어준다.
caculateDifference()함수에서 visited가 true인 경우는 start 팀에 false인 경우 link 팀에 넣어주었다.

2. 나눠진 팀을 가지고 각 팀의 점수를 getTeamAbility() 함수를 이용하여 구해주었다.
그리고 각각 구해진 팀의 점수를 빼서 절댓값을 구해주었다.

그리고 마지막으로 min인 경우를 찾았다.

 

코드

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

// 스타트와 링크
// 능력치 차이의 최소화
public class Main {

	static int N; // 사람수(4~20, 짝수)
	static int[][] S; // 능력치
	static boolean[] visited;
	
	static int minDifference = Integer.MAX_VALUE;
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		N = Integer.parseInt(br.readLine());
		
		S = new int[N+1][N+1];
		visited = new boolean[N+1];
		String str;
		StringTokenizer st;
		for(int i=1; i<N+1; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			for(int j=1; j<N+1; j++) {
				S[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		// ========= 입력 끝 ==========
		
		dfs(0, 0);
		
		System.out.println(minDifference);
	}
	
	static void dfs(int v, int len) {
		if(len == N/2) {
			// 스타트팀, 링크팀 능력치 차이 구하기
			int difference = caculateDifference();
			// 능력치 차이 최솟값 구하기
			minDifference = Math.min(minDifference, difference);
		}
		else {
			for(int i=v+1; i<N+1; i++) {
				if(!visited[i]) {
					visited[i] = true;
					dfs(i, len+1);
					visited[i] = false;
				}
			}
		}
	}
	
	// 팀 점수 차이 구하기
	static int caculateDifference() {
		
		int[] start = new int[N/2 + 1];
		int[] link = new int[N/2 + 1];
		
		int si = 1; int li = 1;
		for(int i=1; i<N+1; i++) {
			if(visited[i]) start[si++] = i;
			else link[li++] = i;
		}
		
		// 각 팀 점수 구하기
		int startAbility = getTeamAbility(start);
		int linkAbility = getTeamAbility(link);
		
		int difference = Math.abs(startAbility-linkAbility);
		
		return difference;
	}

	// 각팀 점수 구하기
	static int getTeamAbility(int[] team) {
		
		int teamAbility = 0;
		
		for(int i=1; i<team.length; i++) {
			for(int j=i+1; j<team.length; j++) {
				teamAbility += S[team[i]][team[j]];
				teamAbility += S[team[j]][team[i]];
			}
		}
		
		return teamAbility;
	}
}

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

 

14888번: 연산자 끼워넣기

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

www.acmicpc.net

문제

브루트포스 문제 인데 DFS 탐색을 이용하여 문제를 해결하였다.

 

해결 방안

숫자의 위치는 고정이고 연산자 우선순위도 무시하므로 숫자간 모든 경우의 연산자를 넣어 각각의 sum값들을 비교하여 최대 최소값을 구하는 문제이다.

덧셈, 뺄셈, 곱셈, 나눗셈의 갯수의 합이 N-1 개이므로 각각의 갯수들을 고려하여 DFS 탐색을 해주면된다.
그리고 기저사례를 depth가 N-1인 경우로 하며 계산된 sum값의 max, min 값을 구해준다.

코드

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

// 연산자 끼워넣기
// 연산자 우선 순위 무시하고 앞에서부터 진행
// 나눗셈은 몫만 취함(음수 이면 양수로 바꾼뒤 몫을 취하고 그 몫을 음수로 바꿈)
// 식의 결과가 최대인 값과 최소인 값을 구함
public class Main {

	static int N; // 수의 갯수(2~11)
	static int[] a; // a1~an
	
	static int[] operatorCnts; // 덧셈, 뺄셈, 곱셈, 나눗셈
	
	// (-10억~10억)
	static int max = Integer.MIN_VALUE;
	static int min = Integer.MAX_VALUE;
	
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(br.readLine());
		a = new int[N+1];
		
		String str;
		str = br.readLine();
		StringTokenizer st = new StringTokenizer(str, " ");
		for(int i=1; i<N+1; i++) {
			a[i] = Integer.parseInt(st.nextToken());
		}
		
		str = br.readLine();
		operatorCnts = new int[4];
		for(int i=0; i<4; i++) {
			operatorCnts[i] = Integer.parseInt(str.split(" ")[i]);
		}
		
		// =========== 입력 끝 ============
		dfs(0, a[1]);
		
		System.out.println(max);
		System.out.println(min);
	}
	
	static void dfs(int depth, int sum) {

		if(depth >= N-1) {
			max = Math.max(max, sum);
			min = Math.min(min, sum);
			return;
		}
		else {
			// 덧셈
			if(operatorCnts[0] > 0) {
				operatorCnts[0]--;
				dfs(depth+1, sum+a[depth+2]);
				operatorCnts[0]++;
			}
			// 뺄셈
			if(operatorCnts[1] > 0) {
				operatorCnts[1]--;
				dfs(depth+1, sum-a[depth+2]);
				operatorCnts[1]++;
			}
			// 곱셈
			if(operatorCnts[2] > 0) {
				operatorCnts[2]--;
				dfs(depth+1, sum*a[depth+2]);
				operatorCnts[2]++;
			}
			// 나눗셈
			if(operatorCnts[3] > 0) {
				operatorCnts[3]--;
				if(sum < 0) {
					sum *= -1;
					dfs(depth+1, (sum/a[depth+2]) * -1);
					sum *= -1;
				}
				else {
					dfs(depth+1, sum/a[depth+2]);
				}
				operatorCnts[3]++;
			}
		}
	}
}

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

 

14503번: 로봇 청소기

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어

www.acmicpc.net

문제

DFS 문제라고 생각하고 풀었는데 stackoverflow가 자꾸 떠서 다른 사람 풀이 참고하여 풀었다. (알고보니 구현 문제)

 

해결 방안

문제에서의 로봇 청소기 작동 방법에 맞춰서 구현해주면 된다.

while 두개를 이용하여 구현하였으며 코드를 보면 1, 2 조건에 따른 코드를 구현하였다. 

 

코드

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

// 로봇 청소기
// 청소하는 영역의 갯수를 구하는 프로그램
// 청소기는 바라보는 방향이 있음
// (r,c) r: 북쪽으로부터 떨어진 칸의 갯수, c: 서쪽으로부터 떨어진 칸의 갯수
// 현재 방향을 기준으로 왼쪽 방향부터 차례대로 탐색을 진행함
public class Main {

	static int N; // 세로 (3~50)
	static int M; // 가로 (3~50)
	static int[][] map;
	
	static int r; // 로봇청소기 좌표(r,c)
	static int c; 
	static int d; // 바라보는 방향 (0:북, 1:동, 2:남, 3:서)
	static int[] dx = {-1, 0, 1, 0};
	static int[] dy = {0, 1, 0, -1};
	static int cleanSite = 0;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str;

		str = br.readLine();
		N = Integer.parseInt(str.split(" ")[0]);
		M = Integer.parseInt(str.split(" ")[1]);
		map = new int[N][M];
		
		str = br.readLine();
		r = Integer.parseInt(str.split(" ")[0]);
		c = Integer.parseInt(str.split(" ")[1]);
		d = Integer.parseInt(str.split(" ")[2]);
		
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			for(int j=0; j<M; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}
		// ========= 입력 끝 ==========
		
		cleaning();
		System.out.println(cleanSite);
	}
	
	static void cleaning() {
		
		int dirCnt = 0; 
		int nx,ny;
		boolean flag = true;
		
		while(flag) {
			// 1. 현재 위치 청소
			if(map[r][c] == 0) {
				map[r][c] = 2;
				cleanSite++;
			}
			
			// 2. 탐색
			while(true) {
				if(dirCnt == 4) {
					nx = r - dx[d];
					ny = c - dy[d];
					
					// d. 뒷칸이 벽인 경우 작동 멈춤
					if(map[nx][ny] == 1) {
						flag = false;
						break;
					}
					// c. 후진
					else if(map[nx][ny] == 2) {
						r = nx;
						c = ny;
						dirCnt = 0;
					}
				}
				
				d--;
				if(d == -1)
					d = 3;
				nx = r + dx[d];
				ny = c + dy[d];
				
				// a. 한칸 전진
				if(map[nx][ny] == 0) {
					dirCnt = 0;
					r = nx;
					c = ny;
					break;
				}
				// b. 2번으로 돌아감
				else {
					dirCnt++;
					continue;
				}
				
			}
		}
	}

}

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

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크�

www.acmicpc.net

문제

벽 3개 세우는 경우를 완전 탐색하고 벽이 3개 세워진 경우에 바이러스를 퍼트려서 최대 안전 구역 크기를 구해주는 문제이다.

생각보다 쉽게 풀려서 놀랐다. DFS + BFS 문제

 

해결 방안

1. 메인에서 지도값 입력받을 때 바이러스의 좌표를 저장할 수 있는 자료구조(여기서는 ArrayList 사용함)를 사용해준다. (나중에 BFS 탐색에서 Queue에 넣어줄 값)

2. DFS 탐색을 이용하여 벽이 3개가 추가된 경우를 완전탐색한다.(벽이 3개가 추가된 지도가 만들어진다.)

3. DFS 탐색의 기저 사례에 벽이 3개가 추가된 경우로 설정해주고 해당 경우마다 바이러스를 BFS 탐색을 이용하여 퍼트려준다.
3.1. 이 때, 해당 지도를 copy한 지도를 만들어준다. (기존 지도는 DFS를 통해 계속 사용해야하므로)
3.2. copy된 지도에 BFS 탐색을 이용하여 바이러스를 퍼트려준다.
3.3. 바이러스가 퍼트려진 copy된 지도의 안전 구역 크기를 구해준다.
3.4. 기존에 누적된 max 안전 구역 크기와 비교하여 새로운 max 안전 크기 구역을 구해준다.

4. 바이러스가 퍼지는 부분을 BFS를 이용하여 구해주었다. (3.2 부분)
4.1. 3.2 부분에서 바이러스를 퍼트릴때 1.에서 저장한 바이러스 좌표를 Queue에 넣어주고 visited를 true로 만들어준다.
4.2. 바이러스 좌표의 상하좌우 부분의 경우 범위가 지도 범위를 넘어가는지 확인해주고 방문 안했으며 빈 칸인 경우 바이러스를 확진 시켜준다.
4.3. BFS 탐색이 끝나면 해당 map을 반환해준다.
4.4. 끝나면 3.2 과정이 끝나 3.3 과정이 시작한다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

// 연구소
// 바이러스 확산을 막기 위해서 연구소에 벽을 세우려고함
// NxM 직사각형의 연구소
// 바이러스는 인접한 빈칸(상하좌우)으로 모두 퍼져나감
// 새로 세울 수 있는 벽의 갯수는 3개
// 0:빈칸  1:벽  2:바이러스
// 안전 영역(바이러스, 벽 제외한 부분) 크기의 최댓값을 구하는 프로그램 작성
public class Main {

	static int N; // 세로 (3~8)
	static int M; // 가로 (3~8)
	static int[][] maps; // 지도
	static boolean[][] visited;
	
	// 바이러스 움직임 (상하좌우)
	static int[] dx = {-1, 1, 0, 0}; // 세로
	static int[] dy = {0, 0, -1, 1}; // 가로
	
	static List<int[]> virusLocation = new ArrayList<>(); // 바이러스 좌표
	
	static int max_safe = 0;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		String str;
		str = br.readLine();
		N = Integer.parseInt(str.split(" ")[0]);
		M = Integer.parseInt(str.split(" ")[1]);
		
		maps = new int[N][M];
		visited = new boolean[N][M];
		
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str, " ");
			for(int j=0; j<M; j++) {
				maps[i][j] = Integer.parseInt(st.nextToken());
				
				if(maps[i][j] == 2) {
					virusLocation.add(new int[] {i, j});
				}
			}
		}
		//====== 입력 끝 ========
		
		// 완전 탐색으로 3개의 벽 세우고 바이러스 퍼졌을 때 안전지역이 최대값을 찾는 프로그램
		dfs(0);
		
		
		System.out.println(max_safe);

	}
	
	public static int[][] copyMap() {
		int[][] copy_maps = new int[N][M];
		
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				copy_maps[i][j] = maps[i][j];
			}
		}
		
		return copy_maps;
	}
	
	//바이러스 퍼지는 것을 bfs로 구현
	public static int[][] bfs(int[][] copy_maps, boolean[][] visited) {
		
		Queue<int[]> q = new LinkedList<>();
		
		// 처음 바이러스좌표를 Queue에 넣어줌
		int[] loc = new int[2];
		for(int i=0; i<virusLocation.size(); i++) {
			loc = virusLocation.get(i);
			q.offer(new int[] {loc[0], loc[1]});
			visited[loc[0]][loc[1]] = true;
		}
		
		// 현재 바이러스 위치
		int cx, cy;
		int nx, ny;
		int[] tempLoc;
		while(!q.isEmpty()) {
			tempLoc = q.poll();
			cx = tempLoc[0];
			cy = tempLoc[1];
			
			// 상하좌우 바이러스 퍼트림
			for(int i=0; i<4; i++) {
				nx = dx[i] + cx;
				ny = dy[i] + cy;

				// 범위 넘어가는지 확인
				if(nx<0 || nx>=N || ny<0|| ny>=M) continue;
				
				// 방문 안했고 빈 칸 인지 확인해서 바이러스확진
				if(!visited[nx][ny]) {
					if(copy_maps[nx][ny] == 0) {
						visited[nx][ny] = true;
						copy_maps[nx][ny] = 2;
						q.offer(new int[] {nx, ny});
					}
				}
			}
		}
		
		return copy_maps;
	}
	
	public static void dfs(int cnt) {
		// 벽의 갯수가 3개가 됬을 때
		if(cnt == 3) {
			
			int[][] copy_maps = new int[N][M];
			
			// copy map
			copy_maps = copyMap();
			
			// 바이러스 다 퍼트리고
			boolean[][] visited = new boolean[N][M];
			copy_maps = bfs(copy_maps, visited);
			
			int safe = 0;
			// 안전 지역 갯수 구하고(빈 칸의 갯수)
			for(int i=0; i<N; i++) {
				for(int j=0; j<M; j++) {
					if(copy_maps[i][j] == 0)
						safe++;
				}
			}
			
			// 안전지역 최댓값 구함
			max_safe = Math.max(max_safe, safe);
		}
		else {
			
			// 벽의 갯수 dfs 탐색으로 늘려줌
			for(int i=0; i<maps.length; i++) {
				for(int j=0; j<maps[i].length; j++) {
					// 벽이나 바이러스일 경우 
					if(maps[i][j] == 1 || maps[i][j] == 2) continue;
					// 빈 칸 인 경우
					else {
						// 방문하지 않은 경우
						if(!visited[i][j]) {
							// 빈칸에 벽을 세워줌
							visited[i][j] = true;
							maps[i][j] = 1;
							dfs(cnt+1);
							maps[i][j] = 0;
							visited[i][j] = false;
						}
					}
				}
			}
		}
	}

}

+ Recent posts