4 분 소요

1107번: 리모컨

문제

수빈이는 TV를 보고 있다. 수빈이는 채널을 돌리려고 했지만, 버튼을 너무 세게 누르는 바람에, 일부 숫자 버튼이 고장났다.

리모컨에는 버튼이 0부터 9까지 숫자, +와 -가 있다. +를 누르면 현재 보고있는 채널에서 +1된 채널로 이동하고, -를 누르면 -1된 채널로 이동한다. 채널 0에서 -를 누른 경우에는 채널이 변하지 않고, 채널은 무한대 만큼 있다.

수빈이가 지금 이동하려고 하는 채널은 N이다. 어떤 버튼이 고장났는지 주어졌을 때, 채널 N으로 이동하기 위해서 버튼을 최소 몇 번 눌러야하는지 구하는 프로그램을 작성하시오.

수빈이가 지금 보고 있는 채널은 100번이다.


입력 조건

첫째 줄에 수빈이가 이동하려고 하는 채널 N (0 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 고장난 버튼의 개수 M (0 ≤ M ≤ 10)이 주어진다. 고장난 버튼이 있는 경우에는 셋째 줄에는 고장난 버튼이 주어지며, 같은 버튼이 여러 번 주어지는 경우는 없다.


출력 조건

첫째 줄에 채널 N으로 이동하기 위해 버튼을 최소 몇 번 눌러야 하는지를 출력한다.


첫 번째 시도 .(2022년 2월)

channel = int(input())
m = int(input())

uf_buttons = set(map(int,input().split()))
buttons = set([i for i in range(10)])

buttons= list(buttons - uf_buttons)
buttons=sorted(buttons)

if len(buttons) != 0:
    #case1
    channel_1=0
    for num in str(channel):
        channel_1 *=10
        gap=11
        best_button=11
    
        for button in buttons:
            if gap > abs(int(num)-button):
                gap = abs(int(num)-button)
                best_button = button
        channel_1 += best_button

    if int(str(channel_1)[0]) > int(str(channel)[0]):
        channel_1 = str(channel_1)[0]
        for i in range(len(str(channel))-1):
            channel_1 += str(buttons[0])
        channel_1 = int(channel_1)

    if int(str(channel_1)[0]) < int(str(channel)[0]):
        channel_1 = str(channel_1)[0]
        for i in range(len(str(channel))-1):
            channel_1 += str(buttons[-1])
        channel_1 = int(channel_1)
    
    #case2    
    channel_2=0
    if len(buttons) >= 2:
        if buttons[0]==0:
            channel_2 += buttons[1]
        else:
            channel_2 += buttons[0]
    else:
        channel_2 += buttons[0]
    for i in range(len(str(channel))):
        channel_2 *=10
        channel_2 += buttons[0]

    #case3
    channel_3 = 10000001
    if channel >= 10:
        channel_3=0
        for i in range(len(str(channel))-1):
            channel_3 *=10
            channel_3 += buttons[-1]
    
    #case4
    channel_4=0
    temp = round(channel,-1)
    for num in str(temp):
        channel_4 *=10
        gap=11
        best_button=11
    
        for button in buttons:
            if gap > abs(int(num)-button):
                gap = abs(int(num)-button)
                best_button = button
        channel_4 += best_button

    if int(str(channel_4)[0]) > int(str(temp)[0]):
        channel_4 = str(channel_4)[0]
        for i in range(len(str(temp))-1):
            channel_4 += str(buttons[0])
        channel_4 = int(channel_4)

    if int(str(channel_4)[0]) < int(str(temp)[0]):
        channel_4 = str(channel_4)[0]
        for i in range(len(str(temp))-1):
            channel_4 += str(buttons[-1])
        channel_4 = int(channel_4)

    least_move=[]
    for make_channel in [channel_1,channel_2,channel_3,channel_4]:
        least_move.append(abs(make_channel-channel)+len(str(make_channel)))
    least_move.append(abs(100-channel))
    result = min(least_move)
else:
    result = abs(channel-100)
    
print(result)

»intput

5457
3
6 7 8

»output

6

fail

image-20220501205903457


🌝 Thinking

[아이디어]

  1. 고장 나지 않은 버튼들을 이용하여 가고 싶은 채널에 가장 근사한 숫자를 만든다.

    (가고 싶은 채널의 각자리의 숫자를 가용 버튼과 비교하여 가장 오차가 적은 숫자를 채택하여 채널을 만듬.)

  2. 1에서 생성된 번호와 가고 싶은 채널의 오차는 채널 위/아래 버튼으로 도달 시킴.

  3. 위의 케이스와 100번 채널에서 가고 싶은 채널의 차이 중 최소 횟수를 채택함.

[틀린이유]

1~2에서 생성된 채널은 자릿수에서 자유롭지 못한다.

(예를들면, 1001번을 틀고 싶을 때 999번에서의 접근이 1111에서의 접근보다 좋다. 사용 가능 버튼이 1,9일 때 )

이를 처리해주기 위해서는 효율적이지 못한 코드가 늘어난다.

그 다음 각 자리에서의 오차만 줄이는 접근은 그 숫자로부터 큰 수로부터 접근하는지 작은 수로 접근할지,

예측하기 어렵다.(가능하더라도 효율적이지 못함.)

따라서 반례가 존재했다…

이 풀이방법에 얽매이어 있어서 유사한 풀이코드로는 풀수 없었다……..



두 번째 시도 .(2022년 4월 28일)

#채널/ 고장난 버튼 수/ 고장난 버튼/ 정상 버튼
channel = int(input())
broken_num = int(input())

if broken_num != 0:
    broken = set(map(int,input().split()))
    button = list(set(range(10)) - broken)
else:
    button = list(range(10))
    
click = []
#버튼이 전부다 고장이 났다면? 직접이동
if broken_num == 10:
    print(abs(channel - 100))
#버튼이 전부 멀쩡하다면? 버튼이동 vs 직접이동
elif broken_num == 0:
    if len(str(channel)) < abs(channel - 100):
        print(len(str(channel)))
    else:
        print(abs(channel - 100))
#남은 버튼 vs 직접이동
else:
    #작은 채널
    for make in range(channel,-1,-1):
        channel_len = len(str(make))
        count = 0
        test = make
        if make == 0 and 0 in button:
            click.append(channel + 1)
        while test!=0:
            if test % 10 in button:
                count += 1
                test //= 10
            else:
                break
        if channel_len == count:
            click.append(channel - make + channel_len)
            break
    #큰채널
    for make in range(channel+1,1000001):
        channel_len = len(str(make))
        count = 0
        test = make
        while test!=0:
            if test % 10 in button:
                count += 1
                test //= 10
            else:
                break
        if channel_len == count:
            click.append(make - channel + channel_len)
            break

    #100에서 직접이동
    channel_direct = abs(channel - 100)
    click.append(channel_direct)
    print(min(click))

»intput

5457
3
6 7 8

»output

6

success

image-20220501205848573


🌝 Thinking

[아이디어]

  1. 반례를 피하기 위해서

    모든 버튼이 멀쩡할 때 (버튼 입력(클릭 횟수) vs 100에서 직접 접근)

    모든 버튼이 망가졌을 때 (100에서 직접 접근)

    버튼이 1개 ~ 9개 망가졌을 때 (직접 접근 vs 만들 수 있는 큰 채널 vs 만들 수 있는 작은 채널)

    와 같이 3가지의 큰 분기로 나누어 생각을 하였다. (이게 정말 중요했다..!)

  2. 가고싶은 채널에서 1씩 증가시켜 버튼으로 만들 수 있는 가장 최소값을 찾음(큰 채널)

  3. 0에서 부터 가고 싶은 채널까지 1씩 증가를 시켜 버튼으로 만들 수 있는 가장 큰 값을 찾음 (작은 채널)

upper bound와 lower bound 찾는 느낌 ?! 이 엄청난 시간 초과를 만들어 낼 줄 알았는데 그렇지 않았음.




💡 깨달은 점.

  1. 브루트포스 알고리즘 문제였는데 첫 번째 시도방식은 해당 알고리즘의 이름에 걸맞지 않았다.

    정말 직접 다 찾아서, 확인을 하면 되었다.

  2. 내가 만든 코드가 반례가 많다면, 전체 코드의 큰 분기문을 손보면 방법이 있다는 사실을 알게 되었다.

댓글남기기