반응형

1. 문제요약

2138번: 전구와 스위치 (acmicpc.net)

 

2138번: 전구와 스위치

N개의 스위치와 N개의 전구가 있다. 각각의 전구는 켜져 있는 상태와 꺼져 있는 상태 중 하나의 상태를 가진다. i(1 < i < N)번 스위치를 누르면 i-1, i, i+1의 세 개의 전구의 상태가 바뀐다. 즉, 꺼져

www.acmicpc.net

N개의 스위치와 N개의 전구가 있다. i (1 < i < N) 번 스위치를 누르면 i - 1, i, i + 1 의 세 개의 전구의 상태가 동시에 바뀐다.

(자기 자신을 포함해서 양 옆에 있는 전구가 동시에 바뀐다.) N개의 전구들 현재 상태와 만들고자 하는 상태가 주어졌을 때, 그 상태를 만들기 위해 스위치를 최소 몇 번 누르면 되는지 알아내는 프로그램을 작성하시오.

 

2. 문제예제

 

 

3. 팩트추출

Fact 1 : 자기 자신의 스위치를 바꿀 수 있는 것은 양 옆에 있는 전구이다.

그런데 만약 0 ~ N - 1 까지 순차적으로 실행하게 된다면, 왼쪽에 있는 전구는 오른쪽 전구에 의해 바뀔 수 있으므로 사고 과정에서 생략할 수 있다. 오른쪽 전구로만 컨트롤하게 바꾼다면 이전 정보가 필요 없어지게 된다. 또 부분문제이기 때문에 그리디 알고리즘으로 풀 수 있다.

 

Fact 2 : i - 1 번째 전구만 보면서 타겟 전구와 같다면 넘기고, 다르다면 스위치를 누른다. 그런데 가장 첫 번째 전구는 눌러야 할지, 말아야 할지 결정할 수가 없기 때문에 두 가지 경우의 수 모두 구한다.

 

Fact 3 : i 가 마지막 N 위치까지 왔는데 타겟 전구의 상태와 다르다면, 도달할 수 없다는 뜻이고 같다면 최소의 경우의 수 이므로 count 를 반환한다. 이 전략이 항상 최선의 상태를 반환하는지는(최소 값을 보장하는지는) 아래 문제풀이(수동)을 통해 알아본다.

 

4. 문제풀이(수동)

그리디 알고리즘은 해당 전략이 항상 최선의 상태를 반환해주는지 검증해야 한다. 문제를 전구 배열 3 크기로 나누어서 생각해본다.

 

현재 전구 상태 : 010

목표 전구 상태 : 111

 

목표 전구 상태와 다른 부분을 보고 바로 킨다면 010 -> 100 -> 011 루틴으로 가장 첫 번째 전구를 못 키게 된다.

그 다음 인덱스에서 킨다면 101 -> 110 -> 111 로 안정적으로 킬 수 있게 된다.

 

현재 전구 상태 : 000

목표 전구 상태 : 111

 

중간에 있는 전구 하나만 키면 되는 것처럼 보이지만 그 다음 인덱스에서 키기 때문에 이 중간 문제도 해당 공식이 성립한다. 이처럼 3 전구 상태 배열의 모든 경우의 수를 나열하고 테스트 해보았을 때, 오른쪽에 있는 전구를 킬 때가 가장 올바른 선택이라는 것을 알 수 있다.

 

5. 문제전략

문제 전략은 예제 풀이(수동)과 팩트 추출을 통해 알아보았다.

 

6. 소스코드

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

public class Main {
    static int N;
    static char lightArray[][], targetArray[];
    static int answer = Integer.MAX_VALUE;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        lightArray = new char[2][N];
        lightArray[0] = br.readLine().toCharArray();
        lightArray[1] = br.readLine().toCharArray();
        targetArray = br.readLine().toCharArray();
        System.out.println(answer == Integer.MAX_VALUE ? -1 : answer);
    }

    private static void run(int index, int count, int type) {
        if(index == N) {
            if(lightArray[type][index-1] == targetArray[index-1]) {
                answer = Math.min(answer, count);
            }
            return;
        }
        if(lightArray[type][index-1]!=targetArray[index-1]) {
            push(index,type);
            run(index+1,count+1,type);
        } else {
            run(index + 1, count, type);
        }
    }
    private static void push(int index, int type) {
        for(int i=index-1; i<=index+1; i++) {
            if(i>=0 && i<N)
                lightArray[type][i] = lightArray[type][i] == '1' ? '0' : '1';
        }
    }
}

 

반응형

'알고리즘 > BOJ' 카테고리의 다른 글

[BOJ] 10999 구간 합 구하기 2  (0) 2022.08.30
[BOJ] 9426 중앙값 측정  (0) 2022.08.19
[BOJ] 6549 히스토그램에서 가장 큰 직사각형  (0) 2022.08.17
[BOJ] 2096 내려가기  (0) 2022.08.11
[BOJ] 13263 나무 자르기  (0) 2021.05.01

+ Recent posts