[경사로 _ 14890]
* 조건
- 행, 열 기준으로 한줄로 이동해나갈 때,
- 기준점의 높이와 다음 칸의 높이 차이가 1을 초과하면 해당 경로는 지나갈 수 없다
- 높이 차이가 0이면 평면이기 때문에 그대로 이동
- 높이 차이가 1이라면
- 아래에서 위로 경사로를 설치하는 경우
- 위에서 아래로 경사로를 설치하는 경우
로 나눠야한다
- 경사로는 설치된 곳에는 다시 설치 불가능하다
- 경사로의 길이(L)만큼의 공간이 없다면 설치 불가능하다
- 지나갈 수 있는 경로의 개수를 출력한다
* 알고리즘
- 이동을 하고 경사로를 설치하는 과정을 진행: 시뮬레이션
* 로직(Logic)
- 우선 (0,0)을 기준으로 행을 하나씩 이동시켜 경로를 지나갈 수 있는지 확인한다 (ex. (0,0) -> (1,0) -> (2,0) ...)
- 하나씩 움직이면서 현재 좌표의 높이와 움직인 좌표의 높이를 구한다
- 높이의 차를 구하고
-> 높이 차이가 2이상이면 해당 경로는 지나갈 수 없음
-> 높이 차이가 0이면 평면이기 때문에 다음 좌표로 이동이 가능
-> 높이 차이가 1이면 아래에서 위로 설치하는지 or 위에서 아래로 설치하는지의 경우를 나눠준다
- 설치를 하는 과정에서 설치된 좌표는 마킹을 해준다 (조건-2)
- 아무런 문제없이 통과된다면 count++
- 열도 마찬가지로 진행한다
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Problem_14890 {
static int N, L;
static int[][] map;
static int count = 0;
//아래, 오른
static int[] dy = {1, 0};
static int[] dx = {0, 1};
static boolean[][] checked;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
String config = br.readLine();
st = new StringTokenizer(config, " ");
N = Integer.parseInt(st.nextToken());
L = Integer.parseInt(st.nextToken());
map = new int[N][N];
for(int i=0; i<N; ++i){
String input = br.readLine();
st = new StringTokenizer(input, " ");
for(int j=0; j<N; ++j){
map[i][j] = Integer.parseInt(st.nextToken());
}
}
//행의 기준을 한칸씩 늘린 경우
for(int i=0; i<N; ++i){
checked = new boolean[N][N];
int j = 0;
int dir = 1;
doProcess(i, j, dir);
}
//열의 기준을 한칸씩 늘린 경우
for(int j=0; j<N; ++j){
checked = new boolean[N][N];
int i = 0;
int dir = 0;
doProcess(i, j, dir);
}
System.out.println(count);
}
private static void doProcess(int i, int j, int dir) {
//사람을 이동시키기 -> 이동시키다가 map의 높이차이가 1을 초과하면 x, 높이차이가 1이면 L의 길이를 고려해 높이가 유지되는지 확인 후 -> 가능하면 L만큼 이동
if(move(i, j, dir)){
count++;
}
}
private static boolean move(int i, int j, int dir) {
while (true){
int moveY = i + dy[dir];
int moveX = j + dx[dir];
if(!isRanged(moveY, moveX)) return true;
//1을 초과하면 false
if(Math.abs(map[i][j] - map[moveY][moveX]) > 1){
return false;
}
if(Math.abs(map[i][j] - map[moveY][moveX]) == 0){
i = moveY;
j = moveX;
//continue;
}
//높이차이가 1이라면
else if(Math.abs(map[i][j] - map[moveY][moveX]) == 1){
int tmpY = 0, tmpX = 0;
int baseY;
int baseX;
//L 길이만큼 경사로를 둘 수 있는지 판별
//경사로를 위 -> 아래로 설치한다면
if(map[i][j] > map[moveY][moveX]){
baseY = moveY;
baseX = moveX;
if(checked[baseY][baseX]){
return false;
}
checked[baseY][baseX] = true;
for(int a=0; a<L-1; ++a){
tmpY = baseY + dy[dir];
tmpX = baseX + dx[dir];
if(isRanged(tmpY, tmpX)){
if(Math.abs(map[baseY][baseX] - map[tmpY][tmpX]) > 0){
return false;
}
//높이가 유지된다면 경사로를 놓은 좌표로 이동
else if(Math.abs(map[baseY][baseX] - map[tmpY][tmpX]) == 0){
baseY = tmpY;
baseX = tmpX;
checked[baseY][baseX] = true;
}
}
else return false;
}
i = baseY;
j = baseX;
}
//경사로를 아래 -> 위로 설치한다면 -> 이전의 높이가 동일한 좌표들이 L개 있어야함
else if(map[i][j] < map[moveY][moveX]){
baseY = i;
baseX = j;
if(checked[baseY][baseX]){
return false;
}
checked[baseY][baseX] = true;
for(int a=0; a<L-1; ++a){
tmpY = baseY - dy[dir];
tmpX = baseX - dx[dir];
if(isRanged(tmpY, tmpX)){
if(checked[tmpY][tmpX]){
return false;
}
if(Math.abs(map[baseY][baseX] - map[tmpY][tmpX]) > 0){
return false;
}
else if(Math.abs(map[baseY][baseX] - map[tmpY][tmpX]) == 0){
baseY = tmpY;
baseX = tmpX;
checked[baseY][baseX] = true;
}
}
else return false;
}
i = i + dy[dir];
j = j + dx[dir];
}
}
}
}
private static boolean isRanged(int y, int x) {
if(y>=0 && y<N && x>=0 && x<N) return true;
return false;
}
}
[톱니바퀴 _ 14891]
* 조건
- 주어진 명령에 맞는 톱니바퀴와 N<->S를 이룬다면 반대방향으로 회전이 가능하다
- 같은 극을 띈다면 해당 톱니바퀴는 회전이 불가하다
- 12시 방향의 극이 S이면 점수를 얻을 수 있다
* 알고리즘
- 입력된 명령에 맞게 구현: 시뮬레이션
* 로직(Logic)
- 명령을 수행할 해당 톱니를 기준으로 왼쪽, 오른쪽 방향을 탐색하면서 서로 다른 극을 가진다면 인접한 톱니의 회전 가능을 마킹해준다
- 만약 같은 극을 가진다면 마킹을 하지 않는다
- 회전을 실행할 때 역시 명령이 시작될 톱니를 기준으로 왼쪽, 오른쪽으로 탐색을 진행한다
- 만약 회전 가능 마킹이 되어있다면 반대 방향으로 회전이 이루어지도록 한다
- 명령이 모두 끝났다면, 배열 인덱스를 기준으로 맨 처음 인덱스 값이 12시 방향이기 때문에 각 톱니 별로 [0] 인덱스 부분을 누적해서 값을 구한다
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.StringTokenizer;
public class Problem_14891 {
static StringTokenizer st;
static int size = 8;
static LinkedList<String>[] gears = new LinkedList[size];
static int operNum;
static int operIdx = 0;
static ArrayList<String> operations = new ArrayList<>();
static boolean[] checked; //N<->S이면 해당 톱니바퀴를 마킹
static boolean leftMark;
static boolean rightMark;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
for(int i=0; i<4; ++i){
String input = br.readLine();
gears[i] = new LinkedList<>();
for(int j=0; j<size; ++j){
gears[i].add(input.substring(j, j+1));
}
}
//명령 입력
operNum = Integer.parseInt(br.readLine());
for(int i=0; i<operNum; ++i){
String input = br.readLine();
operations.add(input);
}
//명령이 입력된 톱니를 기준으로 왼쪽으로 인접해있는 톱니 && 오른쪽으로 인접해있는 톱니를 탐색
//(왼쪽)만약 N<->S처럼 되어있다면 해당 톱니를 마킹해주고 -> 해당 톱니의 왼쪽에 인접한 톱니를 다시 탐색 -> 만약 같은 극이라면 종료
//(오른쪽) 마찬가지로 진행
//명령이 입력된 톱니를 먼저 돌리고
//(왼쪽) 마킹이 되어있다면 반대 방향으로 회전시킴 -> 마킹이 없다면 종료
//(오른쪽)도 마찬가지로 진행
doProcess();
int acc = 0;
int weight = 1;
for(int i=0; i<4; ++i){
if(gears[i].get(0).equals("1")){
acc = acc + weight;
}
weight *= 2;
}
System.out.println(acc);
}
private static void doProcess() {
for(int i=0; i<operations.size(); ++i){
String input = operations.get(operIdx++);
st = new StringTokenizer(input, " ");
int gearIdx = Integer.parseInt(st.nextToken()) - 1;
int dir = Integer.parseInt(st.nextToken());
checked = new boolean[4];
checked[gearIdx] = true;
findInterLock(gearIdx);
//기준 톱니 회전
doRotate(gearIdx, dir);
int leftIdx = gearIdx;
int rightIdx = gearIdx;
int leftDir = dir, rightDir = dir;
leftMark = false;
rightMark = false;
while (true){
if(leftMark && rightMark) {
break;
}
//왼쪽 방향 회전 적용
if(!leftMark){
leftIdx--;
leftDir *= -1;
doRotate(leftIdx, leftDir);
}
//오른쪽 방향 회전 적용
if(!rightMark){
rightIdx++;
rightDir *= -1;
doRotate(rightIdx, rightDir);
}
}
}
}
private static void doRotate(int gearIdx, int dir) {
if(gearIdx < 0) {
leftMark = true;
return;
}
else if(gearIdx >= 4) {
rightMark = true;
return;
}
if(checked[gearIdx]){
switch (dir){
//시계방향 (오른쪽으로 밀어줌)
case 1:
String rear = gears[gearIdx].removeLast();
gears[gearIdx].addFirst(rear);
break;
//반시계방향 (왼쪽으로 밀어줌)
case -1:
String front = gears[gearIdx].removeFirst();
gears[gearIdx].addLast(front);
break;
}
}
}
private static void findInterLock(int index) {
//해당 톱니를 기준으로 왼쪽으로 탐색
findLeft(index);
//해당 톱니를 기준으로 오른쪽으로 탐색
findRight(index);
}
private static void findRight(int index) {
while (true){
int moveIdx = index + 1;
if(moveIdx >= 4){
return;
}
String current = gears[index].get(2);
String moved = gears[moveIdx].get(6);
if(current.equals(moved)){
return;
}
if(!current.equals(moved)){
checked[moveIdx] = true;
}
index = moveIdx;
}
}
private static void findLeft(int index) {
while (true){
int moveIdx = index - 1;
if(moveIdx < 0){
return;
}
String current = gears[index].get(6);
String moved = gears[moveIdx].get(2);
if(current.equals(moved)){
return;
}
if(!current.equals(moved)){
checked[moveIdx] = true;
}
index = moveIdx;
}
}
}
시뮬레이션의 경우 예외 케이스 처리의 중요성을 체감했다. 구현이 다 됐다고 생각이 들더라도, 항상 문제에 주어진 엣지 케이스를 생각하는 습관을 들이면 고생을 덜 할 수 있을 것 같다. 어떤 문제든지 엣지 케이스는 중요하지만 시뮬레이션의 경우 한번 더 생각해보는 습관을 의식적으로라도 해봐야겠다.
'Algorithm > Problem_SW' 카테고리의 다른 글
SW 역량 테스트 기출 문제(아기상어) (1) | 2019.10.18 |
---|---|
SW 역량 테스트 기출 문제(미세먼지 안녕) (0) | 2019.10.16 |
SW 역량 테스트 기출 문제(연산자 끼워넣기, 스타트와 링크) (0) | 2019.10.15 |
SW 역량 테스트 기출 문제(로봇 청소기) (1) | 2019.10.10 |
SW 역량 테스트 기출 문제(퇴사, 연구소) (0) | 2019.10.10 |