반응형

 

www.acmicpc.net/problem/10816

 

10816번: 숫자 카드 2

첫째 줄에 상근이가 가지고 있는 숫자 카드의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 둘째 줄에는 숫자 카드에 적혀있는 정수가 주어진다. 숫자 카드에 적혀있는 수는 -10,000,000보다 크거나 같고, 10,

www.acmicpc.net

지난번에 풀었던 10815번 숫자카드 1 문제의 상위 호환 문제이다.

숫자카드1 문제의 경우 이진탐색을 하여 찾고자 하는 숫자가 있으면 단순히 1을 출력하는 문제였다면,

이 문제는 찾고자 하는 숫자의 개수까지 구해야 한다.

 

<방법1>

처음에는 숫자카드 1처럼 이진탐색을 한 후에 찾은 숫자는 리스트에서 제외를 해주고 다시 이진탐색을 추가하는 식으로 해서 개수를 구했는데, 이진탐색임에도 불구하고 시간초과 오류가 났다.

 

<방법2>

그래서 다음으로 찾은 방법은

이진 탐색을 진행하여 찾고자하는 숫자를 찾게되면 그 숫자만 count함수를 이용해서 개수를 구한 방법이다.

코드는 첨부하지만, 이 방법도 40%쯤 지나갔을 때 시간초과 오류가 발생했다.

from sys import stdin
n = stdin.readline().rstrip()
card = list(map(int,stdin.readline().split()))
m = stdin.readline().rstrip()
test = list(map(int,stdin.readline().split()))

card.sort()

def binary_search(array, target, start, end):
    if start > end:
        return None
    mid = (start + end) // 2
    if array[mid] == target:
        return array[start:end+1].count(target)
    elif array[mid] < target:
        return binary_search(array, target, mid+1, end)
    else:
        return binary_search(array, target, start, mid-1)

for i in range(len(test)):
    a = binary_search(card, test[i], 0, len(card)-1)
    if a is not None:
        print(a, end=' ')
    else:
        print(0, end=' ')

 

<방법3>

그래서 다음으로 찾은 방법은,

파이썬 내장 모듈인 bisect를 이용해서 풀었고, 시간초과 오류 없이 통과하였다.

bisect는 파이썬에 내장되어있는 이진탐색 모듈인데

bisect_left(list, value)는 list에서 value가 들어갈 가장 왼쪽 인덱스,

bisect_right(list, value)는 list에서 value가 들어갈 가장 오른쪽 인덱스를 알려준다.

 

이를 이용하여 count_by_range라는 함수를 만들어 주었는데

이 함수의 인자인 left_value와 right_value 사이에 존재하는 숫자들의 갯수를 반환하는 함수이다.

따라서 left_value와 right_value에 같은 값을 주게 되면 그 숫자의 갯수를 세게 되는 것이다.

from bisect import bisect_left, bisect_right
from sys import stdin

n = stdin.readline().rstrip()
card = list(map(int,stdin.readline().split()))
m = stdin.readline().rstrip()
test = list(map(int,stdin.readline().split()))
card.sort()

def count_by_range(a, left_value, right_value):
    right_index = bisect_right(a, right_value)
    left_index = bisect_left(a, left_value)
    return right_index - left_index


for i in range(len(test)):
    print(count_by_range(card, test[i], test[i]), end=' ')

 

<방법4>

파이썬 내장 모듈인 Counter 이용

Counter는 리스트를 값으로 주게 되면 해당 원소들이 몇 번 등장했는지 세어 딕셔너리 형태로 반환한다.

from collections import Counter
from sys import stdin

n = stdin.readline().rstrip()
card = list(map(int,stdin.readline().split()))
m = stdin.readline().rstrip()
test = list(map(int,stdin.readline().split()))

count = Counter(card)

for i in range(len(test)):
    if test[i] in count:
        print(count[test[i]], end=' ')
    else:
        print(0, end=' ')

 

<방법5>

해쉬맵 이용 ( 파이썬에서는 딕셔너리 )

방법4의 Counter와 비슷하지만 직접 해쉬맵을 만들어 구현하였다.

from sys import stdin

n = stdin.readline().rstrip()
card = list(map(int,stdin.readline().split()))
m = stdin.readline().rstrip()
test = list(map(int,stdin.readline().split()))

hash = {}

for i in card:
    if i in hash:
        hash[i] += 1
    else:
        hash[i] = 1

for i in test:
    if i in hash:
        print(hash[i], end=' ')
    else:
        print(0, end=' ')

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

 

 

반응형
반응형

www.acmicpc.net/problem/5212

 

5212번: 지구 온난화

첫째 줄에 지도의 크기 R과 C (1 ≤ R, C ≤ 10)가 주어진다. 다음 R개 줄에는 현재 지도가 주어진다.

www.acmicpc.net

문제 설명

지도 크기 N x M을 입력받은 후에 각각의 좌표 칸 바다(.) 인지 땅(X)인지를 입력한다.

이 때, 50년 후의 섬 주변( 상, 하, 좌, 우) 으로 3개 이상의 바다가 존재한다면 그 땅은 바다로 변하게 된다.

또한 바다만 있는 줄이나 칸은 모두 사라진다. ( 맵의 바깥쪽은 모두 바다이기 때문에 바다만 있는 줄, 칸은 생략된다)

따라서 50년 후에는 당연히 맵의 크기는 줄어들 것이다.

이 때, 50년 후의 지도를 출력하면 된다.

위의 예제로 예를 들면, (3,0)의 X는 위, 왼쪽, 아래가 모두 바다이기 때문에 .으로 변하게 된다.

또한, X=0 ( 첫 번째 줄)은 모두 바다이기 때문에 아예 없애준다.


풀이 과정

이 문제는 구현(Implementation) 문제이다.

각 칸마다 DFS / BFS 검사를 하듯이 상하좌우로 검사를 해준다. ( 범위를 벗어나면 오류가 나므로 범위 지정은 필수 )

 

new_graph가 변환 후의 맵인데, deepcopy를 해준 이유는 그냥 복사(=)를 하게 되면 한 맵을 수정하게 되면 두 맵이 같이 바뀌게 된다. 따라서 deepcopy를 해주어 기존 비교용 맵과 새로 바꿀용 맵을 바꾸어 준 것이다.

 

현재 땅을 기준으로 상하좌우에 바다가 3칸 이상 존재하면 현재 땅을 바다로 만들어 준다.

그리고 첫 번째 X가 나타나는 줄을 start_row, 마지막 X가 나타나는 줄을 end_row로 지정하여

줄여진 맵의 행수를 정해주며

마찬가지로 칸 수도 같은 방식으로 진행하여, 줄여진 맵의 열의 수를 정해준다.

import copy
r, c = map(int, input().split())

graph = []
for i in range(r):
    graph.append(list(input()))
new_graph = copy.deepcopy(graph)

dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]

for x in range(r):
    for y in range(c):
        if graph[x][y] == '.':
            continue
        sea_count = 0
        for k in range(4):
            nx = x + dx[k]
            ny = y + dy[k]
            if nx < 0 or nx >= r or ny < 0 or ny >= c:
                sea_count += 1
                continue
            elif graph[nx][ny] == '.':
                sea_count += 1

        if sea_count >= 3:
            new_graph[x][y] = '.'

start_row = 0
start_col = 0
end_row = 0
end_col = 0
min_index = c-1
max_index = 0

for i in range(r):
    if 'X' in new_graph[i]:
        start_row = i
        break

for i in range(r-1, -1, -1):
    if 'X' in new_graph[i]:
        end_row = i
        break

for i in range(start_row,  end_row+1):
    tmp = [i for i, value in enumerate(new_graph[i]) if value == 'X']
    if not tmp:
        continue
    min_tmp = tmp[0]
    max_tmp = tmp[-1]
    min_index = min(min_index, min_tmp)
    max_index = max(max_index, max_tmp)

for i in range(start_row, end_row+1):
    for j in range(min_index, max_index+1):
        print(new_graph[i][j], end='')
    print()

마지막 쯤에 tmp = [i for i, value in enumerate(new_graph[i]) if value == 'X'] 부분이 헷갈릴 수 있는데,

enumerate는 컬렉션의 원소를 ( 인덱스, 원소값 ) 으로 반환해주는 함수라고 생각하면 된다. 

즉 여기서는 (i, value) 형태로 반환하는데 나는 인덱스만 필요하기 때문에 i만 tmp 리스트에 넣으면 된다.

그래서 칸(열)의 최소값과 최댓값을 구해 맵의 열 수를 구하면 되는 것이다.

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

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

 

3107번: IPv6

첫째 줄에 올바른 IPv6 주소가 주어진다. 이 주소는 최대 39글자이다. 또한, 주소는 숫자 0-9, 알파벳 소문자 a-f, 콜론 :으로만 이루어져 있다.

www.acmicpc.net

문제 설명

IPv6의 주소는 32자리의 16진수를 4자리씩 끊어 나타낸다. 이때, 각 그룹은 콜론 (:)으로 구분해서 나타낸다.

예를 들면, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 이런 식으로 나타낸다.

 

 

문제에서는, 규칙 두가지가 존재한다.

1. 각 그룹의 앞자리의 0의 전체 또는 일부를 생략 할 수 있다. 위의 IPv6을 축약하면, 다음과 같다

2001:db8:85a3:0:00:8a2e:370:7334

 

2. 만약 0으로만 이루어져 있는 그룹이 있을 경우 그 중 한 개 이상 연속된 그룹을 하나 골라 콜론 2개(::)로 바꿀 수 있다.

2001:db8:85a3::8a2e:370:7334

 

2번째 규칙은 모호함을 방지하기 위해서 오직 한 번만 사용할 수 있다.

올바른 축약형 IPv6주소가 주어졌을 때, 이를 원래 IPv6 (32자리의 16진수)로 복원하는 프로그램을 작성해야 한다.

 

예를 들어, 25:09:1985:aa:091:4846:374:bb 라는 입력이 주어졌을 때,

0025:0009:1985:00aa:0091:4846:0374:00bb 이런 식으로 복구하여 출력을 하면 된다.


풀이 과정

1) 입력 문자열을 ":"를 기준으로 나눠 리스트에 담는다. (ip 배열)

2) 각 칸이 4글자이면 복구 전과 후가 같으므로 그대로 옮긴다.

3) 0보다 크고 4글자 보다 작으면 0의 일부만 생략된 것이므로 그 수만큼 0을 추가한다.

4) 0글자이면 경우의 수가 두가지 이다.

    4-1) 문제에서 말한 두 번째 규칙인 연속된 그룹들이 생략된 경우

          -> 남은 칸 수( 8 - len(ip) ) 들을 모두 0으로 채운다.

    4-2) 한 그룹만 생략된 경우

          -> 해당 칸만 0으로 모두 채운다.

 

위의 과정의 소스 코드는 다음과 같다.

ip = list(input().split(":"))

index = 0
ans = ['' for _ in range(8)]
flag = 0

for i in range(len(ip)):
    if len(ip[i]) == 4:
        ans[index] = ip[i]
        index += 1

    elif len(ip[i]) > 0:
        ans[index] = '0' * (4 - len(ip[i])) + ip[i]
        index += 1

    else: # len(ip[i]) == 0
        if flag == 0:
            for j in range(8 - len(ip) + 1):
                ans[index] = '0000'
                index += 1
            flag = 1
        else:
            ans[index] = '0000'
            index += 1

for i in range(len(ans)-1):
    print(ans[i], end=':')

print(ans[-1])

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

 

 

반응형
반응형

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

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변

www.acmicpc.net

문제 설명

링크에서 알 수 있듯이,

좌표 크기 N x M을 입력받은 후에 각각의 좌표 칸 마다 숫자를 부여한다.

 

그리고 우리가 흔히 해봤던 테트리스 게임의 블록 모양들을 이 좌표 위에 올려놓게 되는데

블록이 위치하는 좌표의 숫자들의 합이 최대가 되는 값을 구하면 된다.

즉, 내가 ㅡ(일자 막대) 모양의 블록을 (0,0) (0,1), (0,2), (0,3) 좌표 위에 올려놓았다면

이 네 좌표의 숫자 값을 모두 더해 저장해놓은 후에 이러한 값들의 최댓값을 구하면 되는 것이다.

 


풀이 과정

이 문제는 브루트 포스(Brute-Force) 유형의 구현 문제이다.

모든 칸에 대해 검사를 해보아야 한다.

문제에서 주어진 맵의 크기가 상대적으로 작기 때문에 (4 ≤ N, M ≤ 500)

모든 블록을 모든 칸에 대해 검사하는 방법으로 해결하였다.

 

1. 모든 블록 모양을 각각의 함수로 만든다.

2. 모든 좌표 칸 마다 각각의 함수들의 결과값을 구해 최댓값을 구한다.

 

이 방법 외에도, 모든 블록모양을 담을 수 있는 큰 크기의 2차원 배열을 선언하여

2차원 배열안에 0과 1로 블록 모양을 지정해주는 방식도 있다.

 

편한 방법으로 문제를 해결하면 될 것 같다.

 

코드는 다음과 같다.

n, m = map(int,input().split())
graph = []
ans = 0

def shape1(i, j): # ㅡ
    if j+3 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i][j+2] + graph[i][j+3]

def shape2(i, j): # ㅣ
    if i+3 >= n:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+2][j] + graph[i+3][j]

def shape3(i, j): # L모양
    if i+2 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+2][j] + graph[i+2][j+1]

def shape4(i, j): # L모양 대칭
    if i+2 >= n or j-1 < 0:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+2][j] + graph[i+2][j-1]

def shape5(i, j): # L모양 시계방향 회전
    if i-1 < 0 or j+2 >= m:
        return 0
    return graph[i][j] + graph[i-1][j] + graph[i-1][j+1] + graph[i-1][j+2]

def shape6(i, j): # L대칭 시계방향 회전
    if i+1 >= n or j+2 >=m:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+1][j+1] + graph[i+1][j+2]

def shape7(i, j): # L 시계방향 회전x2
    if i+2 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i+1][j+1] + graph[i+2][j+1]

def shape8(i, j): # L대칭 시계방향 회전x2
    if i+2 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i+1][j] + graph[i+2][j]

def shape9(i, j): # L 시계방향 회전x3
    if i-1 < 0 or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i][j+2] + graph[i-1][j+2]

def shape10(i, j): # L대칭 시계방향 회전x3
    if i+1 >= n or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i][j+2] + graph[i+1][j+2]

def shape11(i, j): # ㅁ모양
    if i+1 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i+1][j] + graph[i+1][j+1]

def shape12(i, j): # 번개모양
    if i+2 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+1][j+1] + graph[i+2][j+1]

def shape13(i, j): # 번개모양 회전
    if i-1 < 0 or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i-1][j+1] + graph[i-1][j+2]

def shape14(i, j): #번개모양 대칭
    if i+2 >= n or j-1 < 0:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+1][j-1] + graph[i+2][j-1]

def shape15(i, j): #번개모양 대칭 회전
    if i+1 >= n or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i+1][j+1] + graph[i+1][j+2]

def shape16(i, j): #ㅗ모양
    if i-1 < 0 or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i-1][j+1] + graph[i][j+2]

def shape17(i, j): #ㅗ모양 회전
    if i+2 >= n or j+1 >= m:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+1][j+1] + graph[i+2][j]

def shape18(i, j): #ㅗ모양 회전x2
    if i+1 >= n or j+2 >= m:
        return 0
    return graph[i][j] + graph[i][j+1] + graph[i+1][j+1] + graph[i][j+2]

def shape19(i, j): #ㅗ모양 회전x3
    if i+2 >= n or j-1 < 0:
        return 0
    return graph[i][j] + graph[i+1][j] + graph[i+1][j-1] + graph[i+2][j]


for i in range(n):
    graph.append(list(map(int,input().split())))

for i in range(n):
    for j in range(m):
        ans = max(ans, shape1(i,j), shape2(i,j), shape3(i,j), shape4(i,j), shape5(i,j), shape6(i,j), shape7(i,j), shape8(i,j), shape9(i,j),
                  shape10(i,j), shape11(i,j), shape12(i,j), shape13(i,j), shape14(i,j), shape15(i,j), shape16(i,j), shape17(i,j), shape18(i,j), shape19(i,j))

print(ans)

 

 

 

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

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

 

8911번: 거북이

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 컨트롤 프로그램이 주어진다. 프로그램은 항상 문제의 설명에 나와있는 네가지 명령으로만 이루어져

www.acmicpc.net

거북이 로봇이 좌표 ( 0, 0 ) 을 시작으로 움직인다. 알파벳으로 명령을 하게 되는데,

  • F: 한 눈금 앞으로
  • B: 한 눈금 뒤로
  • L: 왼쪽으로 90도 회전
  • R: 오른쪽으로 90도 회전 이다.

주의할 점은, L과 R 명령일 때는 움직이지 않고 현재 내 좌표에서 방향만 바꾸는 것이다.

 

각각의 케이스에 대하여, 거북이의 이동좌표를 모두 포함하는 가장 작은 직사각형의 넓이를 출력하면 된다.

즉, (0,0) -> (0,1) -> (0,2) -> (1,2) 로 이동하였다면 정답은 2가 되는 것이다.

 

이 문제는 문제를 보며 해결 과정을 하나씩 발견해가며 그 과정을 소스코드로 바꾸는 '구현(Implementation)' 유형이다.

 

코드는 다음과 같다.

n = int(input())

dx = [0,-1,0,1]
dy = [1,0,-1,0] # 북 서 남 동

for i in range(n):
    pos_x = 0
    pos_y = 0
    pos_dir = 0  # 0북 1서 2남 3동
    move = list(input())
    trace = [(pos_x, pos_y)]
    for j in move:
        if j == 'F':
            pos_x = pos_x + dx[pos_dir]
            pos_y = pos_y + dy[pos_dir]
        elif j == 'B':
            pos_x = pos_x - dx[pos_dir]
            pos_y = pos_y - dy[pos_dir]
        elif j == 'L':
            if pos_dir == 3:
                pos_dir = 0
            else:
                pos_dir += 1
        elif j == 'R':
            if pos_dir == 0:
                pos_dir = 3
            else:
                pos_dir -= 1

        trace.append((pos_x, pos_y))
    width = max(trace, key = lambda x:x[0])[0] - min(trace, key = lambda x:x[0])[0]
    height = max(trace, key = lambda x:x[1])[1] - min(trace, key = lambda x:x[1])[1]
    print(width * height)


궁금한 점이 있으면 언제든지 댓글 남겨주세요

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

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

 

1654번: 랜선 자르기

첫째 줄에는 오영식이 이미 가지고 있는 랜선의 개수 K, 그리고 필요한 랜선의 개수 N이 입력된다. K는 1이상 10,000이하의 정수이고, N은 1이상 1,000,000이하의 정수이다. 그리고 항상 K ≦ N 이다. 그

www.acmicpc.net

문제는 내가 현재 가지고 있는 서로 다른 길이의 랜선들을 잘라서 동일한 길이의 새로운 랜선을 여러개 만드는 것이다.

내가 가지고 있는 랜선의 개수 K와 새로 만들 랜선의 개수 N을 입력받는다.

예를 들어 K=4, N=11 이라면 4개의 긴 랜선을 잘라 11개의 짧은 랜선을 만드는 것이다.

이 때, 11개를 만들 수 있는 랜선의 최대 길이를 구하면 된다.

즉, 11개를 만들기 위해서 랜선을 얼마나 잘라야 하는지를 알면 된다.

 

정답으로 200을 출력한다면 가지고 있는 4개의 랜선으로 새로운 랜선 11개 만들 때 이 새로운 랜선의 최댓값이 200이 된다는 말이다.

 

문제에서 주어진 랜선의 길이는 2^31 - 1보다 작거나 같은 자연수 이기 때문에 절대 앞에서 부터 하나씩 값을 검사하는 순차탐색으로는 시간내에 해결할 수 없다.

 

그래서 이진탐색(Binary Search)을 이용하였다.

내가 가지고 있는 랜선의 길이의 합을 end값으로, 0을 start값으로 설정하여 이 두 값의 절반을 mid값으로 설정한 후, 탐색을 하면 된다. 0과 1600이라면 800부터 검사를 시작하여 문제를 해결해 나가면 된다.

 

순차탐색보다 훨씬 시간도 줄어들고, 시간 초과 문제도 해결할 수 있다.

 

코드는 다음과 같다.

k, n = map(int, input().split())
lan = []

maxcm = 0


def binary_search(array, target, start, end):
    while start <= end:
        moksum = 0
        mid = (start + end) // 2
        if mid == 0:
            return start        # 0값을 나누는 예외상황 처리
        for i in array:
            mok = i // mid
            moksum += mok
        if moksum < target:  # 개수를 더 늘려야 하므로 길이를 줄이자
            end = mid - 1
        elif moksum >= target:  # 개수를 더 줄일 수 있으므로 길이를 늘리자
            start = mid + 1

    return end


for i in range(k):
    lan.append(int(input()))

lansum = sum(lan)  # 입력받은 모든 랜선 길이의 합


result = binary_search(lan, n, 0, lansum) # 길이 배열, 만들어야 하는 랜선수, 시작값, 끝값
print(result)

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

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

 

3020번: 개똥벌레

개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이

www.acmicpc.net

지난번에 이분탐색(Binary Search)으로 풀었던 개똥벌레 문제

https://hongcoding.tistory.com/5

 

[백준] 3020번 개똥벌레 (Python 파이썬)

https://www.acmicpc.net/problem/3020 3020번: 개똥벌레 개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항

hongcoding.tistory.com

이 문제를 누적합 (전위합(Prefix Sum)) 으로 다시 풀었다.

높이 h부터 높이 1까지 누적 합을 계산하면 높이 i의 배열 값은 높이 i 이상의 모든 석순의 개수가 된다.

예를 들어, 높이가 6인 동굴에서 높이 5의 개똥벌레가 날아갈 때, 높이 5 이상의 석순에 모두 부딪히기 때문에

배열 5의 값이 높이 5의 개똥벌레가 부딪히는 석순의 개수가 된다.

 

마찬가지로 종유석은 위에서부터 내려오기 때문에 h - i + 1의 식을 이용하면 된다.

왜냐하면, 높이 6의 동굴에서 높이 2짜리 종유석은 높이 4 (실질적으로 3.5) 위로의 개똥벌레가 모두 부딪히기 때문이다.

즉, 석순처럼 높이2라고 해서 높이 2 밑의 개똥벌레가 부딪히는것이 아니라 위에서 부터기 때문에 반대가 된다.

 

파이썬 코드는 다음과 같다.

n, h = map(int, input().split())

down = [0] * (h + 1)  # 석순
up = [0] * (h + 1)  # 종유석

min_count = n  # 파괴해야 하는 장애물의 최소값
range_count = 0  # 최소값이 나타나는 구간의 수

for i in range(n):
    if i % 2 == 0:
        down[int(input())] += 1
    else:
        up[int(input())] += 1

for i in range(h - 1, 0, -1):
    down[i] += down[i + 1]
    up[i] += up[i + 1]

for i in range(1, h + 1):

    if min_count > (down[i] + up[h - i + 1]):
        min_count = (down[i] + up[h - i + 1])
        range_count = 1
    elif min_count == (down[i] + up[h - i + 1]):
        range_count += 1

print(min_count, range_count)

모르시는 부분이 있으시면 언제든지 댓글 남겨주세요!

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형
반응형

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

 

3020번: 개똥벌레

개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이

www.acmicpc.net

문제에 대한 자세한 설명은 위 링크에 기술 되어있다.

쉽게 풀어쓰자면 동굴의 길이(N)와 높이(H)를 입력받는다.

동굴은 석순과 종유석으로 구성되어 있는데, 석순은 아래에서 자라는 고드름 종유석은 천장에서 나는 고드름이라고 생각하면 된다. 

문제에서 동굴의 길이만큼 석순과 종유석의 길이를 입력받는데, 번갈아 가면서 입력받는다. ( 석순과 종유석은 각각 동굴의 길이 1만큼 차지한다)

예를 들어, 1 3 4 2 5 6 이라고 입력했으면 석순 1, 4, 5 / 종유석 3 2 6 이 되는 것이다.

이렇게 장애물이 설치된 동굴에 개똥벌레 한마리가 날라가는데 동굴의 높이가 6일 때, 개똥벌레가 날아가는 높이가 3이라고 한다면 아래에서부터 높이 2~3 (2.5라고 생각하면 쉽다) 구간을 날라간다. 

그래서 3보다 낮은 석순에는 모두 부딪히게 되고, 3보다 큰 종유석에는 모두 부딪히게 된다. ( 위에서 부터 4 만큼 내려온 종유석에는 아래에서 부터 2.5만큼 날아오른 개똥벌레가 부딪히기 때문)

 

이런 상황에서 문제에서 최종적으로 구하고자 하는 것은, 개똥벌레가 파괴해야 하는 장애물의 최소 개수와 이 최소값이 나오는 구간의 수를 구하는 것이다.

 

그렇다면 어떻게 접근해야 할까?

가장 먼저 떠오른 방법은 석순과 종유석의 배열을 따로 두고, 각각의 배열을 정렬 한 후에 처음 개똥벌레가 부딪히기 시작한 높이를 계산하는 방식이다. 왜냐하면 그 이후의 구간은 모두 부딪힌다는 이야기가 되기 때문에 첫 구간만을 구해 그 구간 전까지의 개수를 통해 답을 구할 수 있기 때문이다. 이러한 접근 방식을 누적 합(또는 구간합)이라고 하는데

현재 페이지에서는 다른 방식으로 풀어보려고 한다.

 

바로 이진탐색(Binary Search) 방식으로 풀려고 한다.

석순과 종유석의 배열이 각각 정렬되어 있기 때문에, 배열의 중간지점에서 개똥벌레가 부딪히는지 테스트를 진행한다.

보통의 이진탐색 과정과 마찬가지로 start값과 end값에 변화를 주며 mid값을 조정하여 개똥벌레가 최소한으로 부딪히는 구간을 찾으면 된다.

 

코드는 다음과 같다.

n, h = map(int, input().split())

down = []
up = []
for i in range(n):
    if i % 2 == 0:
        down.append(int(input()))
    else:
        up.append(int(input()))

down.sort()
up.sort()

min_count = n
range_count = 0


def binary_search(array, target, start, end):
    while start <= end:
        mid = (start + end) // 2

        if array[mid] < target:
            start = mid + 1
        else:
            end = mid - 1

    return start


for i in range(1, h + 1):
    down_count = len(down) - binary_search(down, i - 0.5, 0, len(down) - 1)
    top_count = len(up) - binary_search(up, h - i + 0.5, 0, len(up) - 1)

    if min_count == down_count + top_count:
        range_count += 1
    elif min_count > down_count + top_count: # 현재 범위가 새로운 최소 값이면
        range_count = 1
        min_count = down_count + top_count

print(min_count, range_count)

binary_search함수의 target은 개똥벌레의 높이가 되며 down일 때는 i - 0.5, up일때는 h - i + 0.5를 넣는다.

위에서 자라는 종유석의 경우에는 아래와는 반대로 생각해야 하기 때문이다.

 

https://github.com/HongEunho

 

HongEunho - Overview

📖 Android, Java, Kotlin, Algorithm. HongEunho has 15 repositories available. Follow their code on GitHub.

github.com

전체 문제 & 코드는 위의 깃에 정리되어 있습니다.

팔로우 & 맞팔은 환영입니다 !

반응형

+ Recent posts