문제링크

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

 

12891번: DNA 비밀번호

평소에 문자열을 가지고 노는 것을 좋아하는 민호는 DNA 문자열을 알게 되었다. DNA 문자열은 모든 문자열에 등장하는 문자가 {‘A’, ‘C’, ‘G’, ‘T’} 인 문자열을 말한다. 예를 들어 “ACKA”

www.acmicpc.net

문제설명

문제

평소에 문자열을 가지고 노는 것을 좋아하는 민호는 DNA 문자열을 알게 되었다. DNA 문자열은 모든 문자열에 등장하는 문자가 {‘A’, ‘C’, ‘G’, ‘T’} 인 문자열을 말한다. 예를 들어 “ACKA”는 DNA 문자열이 아니지만 “ACCA”는 DNA 문자열이다. 이런 신비한 문자열에 완전히 매료된 민호는 임의의 DNA 문자열을 만들고 만들어진 DNA 문자열의 부분문자열을 비밀번호로 사용하기로 마음먹었다.

하지만 민호는 이러한 방법에는 큰 문제가 있다는 것을 발견했다. 임의의 DNA 문자열의 부분문자열을 뽑았을 때 “AAAA”와 같이 보안에 취약한 비밀번호가 만들어 질 수 있기 때문이다. 그래서 민호는 부분문자열에서 등장하는 문자의 개수가 특정 개수 이상이여야 비밀번호로 사용할 수 있다는 규칙을 만들었다.

임의의 DNA문자열이 “AAACCTGCCAA” 이고 민호가 뽑을 부분문자열의 길이를 4라고 하자. 그리고 부분문자열에 ‘A’ 는 1개 이상, ‘C’는 1개 이상, ‘G’는 1개 이상, ‘T’는 0개 이상이 등장해야 비밀번호로 사용할 수 있다고 하자. 이때 “ACCT” 는 ‘G’ 가 1 개 이상 등장해야 한다는 조건을 만족하지 못해 비밀번호로 사용하지 못한다. 하지만 “GCCA” 은 모든 조건을 만족하기 때문에 비밀번호로 사용할 수 있다.

민호가 만든 임의의 DNA 문자열과 비밀번호로 사용할 부분분자열의 길이, 그리고 {‘A’, ‘C’, ‘G’, ‘T’} 가 각각 몇번 이상 등장해야 비밀번호로 사용할 수 있는지 순서대로 주어졌을 때 민호가 만들 수 있는 비밀번호의 종류의 수를 구하는 프로그램을 작성하자. 단 부분문자열이 등장하는 위치가 다르다면 부분문자열이 같다고 하더라도 다른 문자열로 취급한다.

입력

첫 번째 줄에 민호가 임의로 만든 DNA 문자열 길이 |S|와 비밀번호로 사용할 부분문자열의 길이 |P| 가 주어진다. (1 ≤ |P| ≤ |S| ≤ 1,000,000)

두번 째 줄에는 민호가 임의로 만든 DNA 문자열이 주어진다.

세번 째 줄에는 부분문자열에 포함되어야 할 {‘A’, ‘C’, ‘G’, ‘T’} 의 최소 개수가 공백을 구분으로 주어진다. 각각의 수는 |S| 보다 작거나 같은 음이 아닌 정수이며 총 합은 |S| 보다 작거나 같음이 보장된다.

출력

첫 번째 줄에 민호가 만들 수 있는 비밀번호의 종류의 수를 출력해라.

예제 입력 1 복사

9 8
CCTGGATTG
2 0 1 1

예제 출력 1 복사

0

예제 입력 2 복사

4 2
GATA
1 0 0 1

예제 출력 2 복사

2
문제풀이

 

핵심

- 문자열에서 특정 조건을 만족하는 크기가 고정된 부분 문자열을 탐색하기 위해 슬라이딩 윈도우 활용

- 딕셔너리를 사용하여 각 필요한 알파벳 충족개수를 비교하는 로직에 활용하였다.

 

시간복잡도를 줄이기 위해 활용하는 슬라이딩 윈도우의 본질을 잊지말자!

(이중반복문 X)

 

반복문 작성시에는 항상 인덱스를 초과하지 않도록 유념해야하는 것 같다.

해당 문제 같은 경우는, 오른쪽 포인터가 인덱스를 벗어나지 않도록 j += 1 문 이전에 

맨 오른쪽 원소를 추가하는 로직을 작성하였다.

 

시간복잡도는 O(N) 이다.

코드
s, p = map(int, input().split())
data = input()
acnt, ccnt, gcnt, tcnt = map(int, input().split())
answer = 0
# 두개의 포인터 지정
i = 0
j = p - 1

# 딕셔너리
dict = { 'A': 0, 'C': 0, 'G': 0, 'T': 0 }
targetStr = data[i:j]

for item in targetStr:
    dict[item] += 1

# 슬라이딩 윈도우
while j <= s-1:
    # 인덱스 초과 나니까 j+=1 하기전에, 오른쪽 원소 추가하는 로직을 먼저.
    dict[data[j]] += 1

    # 비밀번호 유효성 검사
    if dict['A'] >= acnt and dict['C'] >= ccnt and dict['G'] >= gcnt and dict['T'] >= tcnt:
        answer += 1

    dict[data[i]] -= 1 # 인덱스 맨 왼쪽 문자열 제거
    i += 1
    j += 1

print(answer)
문제링크

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

 

2644번: 촌수계산

사람들은 1, 2, 3, …, n (1 ≤ n ≤ 100)의 연속된 번호로 각각 표시된다. 입력 파일의 첫째 줄에는 전체 사람의 수 n이 주어지고, 둘째 줄에는 촌수를 계산해야 하는 서로 다른 두 사람의 번호가 주어

www.acmicpc.net

문제설명

문제

우리 나라는 가족 혹은 친척들 사이의 관계를 촌수라는 단위로 표현하는 독특한 문화를 가지고 있다. 이러한 촌수는 다음과 같은 방식으로 계산된다. 기본적으로 부모와 자식 사이를 1촌으로 정의하고 이로부터 사람들 간의 촌수를 계산한다. 예를 들면 나와 아버지, 아버지와 할아버지는 각각 1촌으로 나와 할아버지는 2촌이 되고, 아버지 형제들과 할아버지는 1촌, 나와 아버지 형제들과는 3촌이 된다.

여러 사람들에 대한 부모 자식들 간의 관계가 주어졌을 때, 주어진 두 사람의 촌수를 계산하는 프로그램을 작성하시오.

입력

사람들은 1, 2, 3, …, n (1 ≤ n ≤ 100)의 연속된 번호로 각각 표시된다. 입력 파일의 첫째 줄에는 전체 사람의 수 n이 주어지고, 둘째 줄에는 촌수를 계산해야 하는 서로 다른 두 사람의 번호가 주어진다. 그리고 셋째 줄에는 부모 자식들 간의 관계의 개수 m이 주어진다. 넷째 줄부터는 부모 자식간의 관계를 나타내는 두 번호 x,y가 각 줄에 나온다. 이때 앞에 나오는 번호 x는 뒤에 나오는 정수 y의 부모 번호를 나타낸다.

각 사람의 부모는 최대 한 명만 주어진다.

출력

입력에서 요구한 두 사람의 촌수를 나타내는 정수를 출력한다. 어떤 경우에는 두 사람의 친척 관계가 전혀 없어 촌수를 계산할 수 없을 때가 있다. 이때에는 -1을 출력해야 한다.

예제 입력 1 복사

9
7 3
7
1 2
1 3
2 7
2 8
2 9
4 5
4 6

예제 출력 1 복사

3

예제 입력 2 복사

9
8 6
7
1 2
1 3
2 7
2 8
문제풀이

 

주어진 친족관계들을 기반으로, 입력 받은 두 값 a, b의 촌수를 계산하는 문제였다.

 

핵심 아이디어

1. 인접리스트 형태로 저장.

2. ★그래프에 저장할때 양방향임을 고려 (주어진 입력값은 한 방향관계만을 나타냄)

3. a, b의 촌수를 구한다는건 => 그래프를 그려놓고 생각해보면 a부터 b까지의 최단거리

   따라서 BFS를 활용

4. 촌수 카운트를 위해 queue에 (노드번호, depth) 튜플형태로 삽입

5. BFS를 시행하다가 구하고자 하는 노드에 도달하면 depth 반환

6. BFS 끝났는데도 도달 안하면 -1 반환

 

코드
from collections import deque

n = int(input())
a, b = map(int, input().split())
m = int(input())
graph = [[] for _ in range(n+1)]
visited = [0] * (n+1)
queue = deque([])

# 양방향 그래프
for _ in range(m):
    s, e = map(int, input().split())
    graph[s].append(e)
    graph[e].append(s)

def BFS(a, b):
    queue.append([a, 0])
    visited[a] = 1

    while queue:
        cur, depth = queue.popleft()

        if cur == b:
            return depth

        for next in graph[cur]:
            if not visited[next]:
                queue.append([next, depth+1])
                visited[next] = 1

    return -1

print(BFS(a, b))
문제링크

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

 

14888번: 연산자 끼워넣기

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

www.acmicpc.net

문제설명

문제

N개의 수로 이루어진 수열 A1, A2, ..., AN이 주어진다. 또, 수와 수 사이에 끼워넣을 수 있는 N-1개의 연산자가 주어진다. 연산자는 덧셈(+), 뺄셈(-), 곱셈(×), 나눗셈(÷)으로만 이루어져 있다.

우리는 수와 수 사이에 연산자를 하나씩 넣어서, 수식을 하나 만들 수 있다. 이때, 주어진 수의 순서를 바꾸면 안 된다.

예를 들어, 6개의 수로 이루어진 수열이 1, 2, 3, 4, 5, 6이고, 주어진 연산자가 덧셈(+) 2개, 뺄셈(-) 1개, 곱셈(×) 1개, 나눗셈(÷) 1개인 경우에는 총 60가지의 식을 만들 수 있다. 예를 들어, 아래와 같은 식을 만들 수 있다.

  • 1+2+3-4×5÷6
  • 1÷2+3+4-5×6
  • 1+2÷3×4-5+6
  • 1÷2×3-4+5+6

식의 계산은 연산자 우선 순위를 무시하고 앞에서부터 진행해야 한다. 또, 나눗셈은 정수 나눗셈으로 몫만 취한다. 음수를 양수로 나눌 때는 C++14의 기준을 따른다. 즉, 양수로 바꾼 뒤 몫을 취하고, 그 몫을 음수로 바꾼 것과 같다. 이에 따라서, 위의 식 4개의 결과를 계산해보면 아래와 같다.

  • 1+2+3-4×5÷6 = 1
  • 1÷2+3+4-5×6 = 12
  • 1+2÷3×4-5+6 = 5
  • 1÷2×3-4+5+6 = 7

N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가 최대인 것과 최소인 것을 구하는 프로그램을 작성하시오.

입력

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

출력

첫째 줄에 만들 수 있는 식의 결과의 최댓값을, 둘째 줄에는 최솟값을 출력한다. 연산자를 어떻게 끼워넣어도 항상 -10억보다 크거나 같고, 10억보다 작거나 같은 결과가 나오는 입력만 주어진다. 또한, 앞에서부터 계산했을 때, 중간에 계산되는 식의 결과도 항상 -10억보다 크거나 같고, 10억보다 작거나 같다.

예제 입력 1 복사

2
5 6
0 0 1 0

예제 출력 1 복사

30
30

예제 입력 2 복사

3
3 4 5
1 0 1 0

예제 출력 2 복사

35
17
문제풀이

 

코드
N = int(input())
arr = list(map(int, input().split()))
add, sub, mul, div = map(int, input().split())
maxValue = - int(1e9)
minValue = int(1e9)

def DFS(add, sub, mul, div, total, i):
    global maxValue, minValue

    if i == N:
        maxValue = max(maxValue, total)
        minValue=  min(minValue, total)
        return
    # 연산자 삽입
    if add:
        DFS(add-1, sub, mul, div, total+arr[i], i+1)
    if sub:
        DFS(add, sub-1, mul, div, total-arr[i], i+1)
    if mul:
        DFS(add, sub, mul-1, div, total*arr[i], i+1)
    if div:
        DFS(add, sub, mul, div-1, int(total/arr[i]), i+1)
            

DFS(add, sub, mul, div, arr[0], 1)
print(maxValue)
print(minValue)
# 연산자 우선순위 무시
# 1 2 3 4 5 6
# + + - * /

# N개의 수와 N-1개의 연산자가 주어질때 만들수있는 결과가 최대인것과 최소인것 구하기
# 완전탐색
문제링크

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

 

3190번: 뱀

'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임

www.acmicpc.net

문제설명

 

문제

'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다.

게임은 NxN 정사각 보드위에서 진행되고, 몇몇 칸에는 사과가 놓여져 있다. 보드의 상하좌우 끝에 벽이 있다. 게임이 시작할때 뱀은 맨위 맨좌측에 위치하고 뱀의 길이는 1 이다. 뱀은 처음에 오른쪽을 향한다.

뱀은 매 초마다 이동을 하는데 다음과 같은 규칙을 따른다.

  • 먼저 뱀은 몸길이를 늘려 머리를 다음칸에 위치시킨다.
  • 만약 벽이나 자기자신의 몸과 부딪히면 게임이 끝난다.
  • 만약 이동한 칸에 사과가 있다면, 그 칸에 있던 사과가 없어지고 꼬리는 움직이지 않는다.
  • 만약 이동한 칸에 사과가 없다면, 몸길이를 줄여서 꼬리가 위치한 칸을 비워준다. 즉, 몸길이는 변하지 않는다.

사과의 위치와 뱀의 이동경로가 주어질 때 이 게임이 몇 초에 끝나는지 계산하라.

입력

첫째 줄에 보드의 크기 N이 주어진다. (2 ≤ N ≤ 100) 다음 줄에 사과의 개수 K가 주어진다. (0 ≤ K ≤ 100)

다음 K개의 줄에는 사과의 위치가 주어지는데, 첫 번째 정수는 행, 두 번째 정수는 열 위치를 의미한다. 사과의 위치는 모두 다르며, 맨 위 맨 좌측 (1행 1열) 에는 사과가 없다.

다음 줄에는 뱀의 방향 변환 횟수 L 이 주어진다. (1 ≤ L ≤ 100)

다음 L개의 줄에는 뱀의 방향 변환 정보가 주어지는데, 정수 X와 문자 C로 이루어져 있으며. 게임 시작 시간으로부터 X초가 끝난 뒤에 왼쪽(C가 'L') 또는 오른쪽(C가 'D')로 90도 방향을 회전시킨다는 뜻이다. X는 10,000 이하의 양의 정수이며, 방향 전환 정보는 X가 증가하는 순으로 주어진다.

출력

첫째 줄에 게임이 몇 초에 끝나는지 출력한다.

예제 입력 1 

6
3
3 4
2 5
5 3
3
3 D
15 L
17 D

예제 출력 1 

9

 

문제풀이

시뮬레이션 문제로,

주어진 조건을 빠르고 정확하게 파악하는 것이 제일 중요한 것 같다.

이 부분은 앞으로도 남들보다 더 꾸준히 문제를 풀어 익숙해지려고 노력해야겠다.

 

핵심

1. 지도에서 움직이는 형태 구현. (상하좌우)

2. 방향 변환하는 함수 만들어서 활용

3. 자료구조 큐 활용

 

주의점

1. 이동후 현재좌표 갱신하기

 

BFS 문제중에 지도에서 움직이는 형태를 구현한 적이 많았는데 갱신된 좌표를 큐에 넣어서 해당좌표를 다시 꺼내 이용하여 문제가 없었지만,

지금은 직접 현재좌표를 갱신하는 추가코드가 필요하다!!

조금 안일하게 문제풀이에 임했지 않았나 싶다...

 

풀이 간략히 설명

- arr을 N * N 으로 만들고 사과가 있는 좌표에 1을 삽입 

- 방향 정보 저장은 딕셔너리를 활용

   direction[시간] = 문자정보  형태

- 뱀이 방문한 좌표는 arr[nx][ny] = 2. 

- 방문한 좌표마다 큐에 삽입하고, 방문한 좌표에 사과가 없다면

큐의 특성을 활용해 꼬리빼기(방문안함으로 처리)

- 이동할때마다 현재좌표 갱신!!

- 매초 마다 time을 증가시켜 direction의 키와 일치하는 시간이 되면 => 방향 변환 (함수로 구현)

 

코드
from collections import deque

N = int(input())
K = int(input())
arr = [[0 for _ in range(N)] for _ in range(N)]
dx = [0, -1, 0, 1]  # 각각 우 상 좌 하
dy = [1, 0, -1, 0]
directions = {}

# 입력값1) 사과 위치 정보 저장
for i in range(K):
    r, c = map(int, input().split())
    arr[r-1][c-1] = 1

L = int(input())
# 입력값2) 방향 변환 정보 저장
for i in range(L):
    x, d = input().split()
    directions[int(x)] = d

def rotateDir(index, str):  # 방향 나타내는 인덱스, 방향
    # 상3 하2 좌1 우0
    # L 왼쪽회전 => 우 -> 상(1) -> 좌(2) -> 하(3)
    # D 오른쪽회전 => 우(0) -> 하(3) -> 좌(2) -> 상(1)
    if str == 'L':
        result = (index + 1) % 4
    elif str == 'D':
        result = (index + 3) % 4
    return result

def start(x, y):
    dir = 0 # 처음은 오른쪽으로 향하니, 동을 나타내는 인덱스0 에 접근 위해 설정.
    queue = deque([(x, y)])
    arr[x][y] = 2
    time = 0
    
    while True:
        time += 1
        nx = x + dx[dir]
        ny = y + dy[dir]
        # 범위안에 있고 방문한 지역 아니라면
        if 0 <= nx < N and 0 <= ny < N and arr[nx][ny] != 2:
            queue.append((nx, ny))
            x, y = nx, ny
            # 사과가 있다면 꼬리 안뺌 그대로 늘림.
            if arr[nx][ny] == 1:
                arr[nx][ny] = 2   # 방문
            else:
                arr[nx][ny] = 2
                px, py = queue.popleft()
                arr[px][py] = 0
        else:
            break

        if time in directions.keys():
            dir = rotateDir(dir, directions[time])
    
    return time

print(start(0, 0))
문제링크

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

 

1759번: 암호 만들기

첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.

www.acmicpc.net

문제설명

 

문제

바로 어제 최백준 조교가 방 열쇠를 주머니에 넣은 채 깜빡하고 서울로 가 버리는 황당한 상황에 직면한 조교들은, 702호에 새로운 보안 시스템을 설치하기로 하였다. 이 보안 시스템은 열쇠가 아닌 암호로 동작하게 되어 있는 시스템이다.

암호는 서로 다른 L개의 알파벳 소문자들로 구성되며 최소 한 개의 모음(a, e, i, o, u)과 최소 두 개의 자음으로 구성되어 있다고 알려져 있다. 또한 정렬된 문자열을 선호하는 조교들의 성향으로 미루어 보아 암호를 이루는 알파벳이 암호에서 증가하는 순서로 배열되었을 것이라고 추측된다. 즉, abc는 가능성이 있는 암호이지만 bac는 그렇지 않다.

새 보안 시스템에서 조교들이 암호로 사용했을 법한 문자의 종류는 C가지가 있다고 한다. 이 알파벳을 입수한 민식, 영식 형제는 조교들의 방에 침투하기 위해 암호를 추측해 보려고 한다. C개의 문자들이 모두 주어졌을 때, 가능성 있는 암호들을 모두 구하는 프로그램을 작성하시오.

입력

첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.

출력

각 줄에 하나씩, 사전식으로 가능성 있는 암호를 모두 출력한다.

예제 입력 1 복사

4 6
a t c i s w

예제 출력 1 복사

acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw
문제풀이

핵심

C개 문자 중  L개를 골라 암호를 만들어야하는데,

1. 최소 2개 자음과 1개 모음을 이용해야함.

2. 알파벳 순서로 나열된 암호여야함

 

풀이 과정

1. arr 오름차순 정렬

2. 정렬된 상태의 배열에서 반복문 시행. 각각 첫번째 원소부터 C-L 번째까지의 원소를 시작점으로 함수시행.

3. 후보리스트에 조건에 맞는 원소 삽입하고 재귀함수 호출

4. 후보리스트 길이가 원하는 길이라면, 암호조건에 부합하는지 체킹하고 리턴

5. 함수시행이 종료됐으므로, 백트래킹하여 다음 순번의 암호찾기.

코드
from itertools import combinations

L, C = map(int, input().split())
arr = list(input().split())
arr.sort()
# 입력예제1 => a, c, i, s, t, w

vowels = set(('a', 'e', 'i', 'o', 'u'))
# 최소 자음2개 모음1개 구성.
def check(arr):
    vcnt, ccnt = 0, 0
    for item in arr:
        if item in vowels:
            vcnt += 1
        else:
            ccnt += 1

    if vcnt >= 1 and ccnt >= 2:
        return True
    else:
        return False
    
def DFS(tmp):
    if len(tmp) == L:
        # 체킹
        if check(tmp):
            print(''.join(tmp))
            return
    
    # a c i s t w
    for i in range(len(tmp), C):
        if tmp[-1] < arr[i]:
            tmp.append(arr[i])
            DFS(tmp)
            tmp.pop()


for i in range(C-L+1):
    tmp = [arr[i]]
    DFS(tmp)

 

문제링크

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

 

1987번: 알파벳

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의 한 칸으

www.acmicpc.net

문제설명

 

문제

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다.

말은 상하좌우로 인접한 네 칸 중의 한 칸으로 이동할 수 있는데, 새로 이동한 칸에 적혀 있는 알파벳은 지금까지 지나온 모든 칸에 적혀 있는 알파벳과는 달라야 한다. 즉, 같은 알파벳이 적힌 칸을 두 번 지날 수 없다.

좌측 상단에서 시작해서, 말이 최대한 몇 칸을 지날 수 있는지를 구하는 프로그램을 작성하시오. 말이 지나는 칸은 좌측 상단의 칸도 포함된다.

입력

첫째 줄에 R과 C가 빈칸을 사이에 두고 주어진다. (1 ≤ R,C ≤ 20) 둘째 줄부터 R개의 줄에 걸쳐서 보드에 적혀 있는 C개의 대문자 알파벳들이 빈칸 없이 주어진다.

출력

첫째 줄에 말이 지날 수 있는 최대의 칸 수를 출력한다.

예제 입력 1 

2 4
CAAB
ADCB

예제 출력 1 

3

예제 입력 2 

3 6
HFDFFB
AJHGDH
DGAGEH

예제 출력 2 

6
문제풀이

백트래킹

DFS의 특성대로 탐색하다가, 더이상 솔루션이 될 수 없다고 판단되면 포기하고 다른 후보를 탐색하는 기법이다.

 

실수한 부분

1. 처음 DFS시행 전에 시작지점 좌표 방문, 알파벳체크 처리 잊지말자!

2. 불필요한 반복검사 줄이기. (아스키코드 활용하여 알파벳 => 숫자 변환하는 로직을 초반에 따로 빼자.)

    -> 이것도 시간초과에 한몫한듯.

 

코드
R, C = map(int, input().split())
arr = [list(input()) for i in range(R)]
visited = [[0 for _ in range(C)] for _ in range(R)] 
checked = [0] * 27
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
answer = 0

# 아스키코드 활용하여 알파벳 => 숫자로 바꾸기
for i in range(R):
    for j in range(C):
        arr[i][j] = ord(arr[i][j]) - 64

def DFS(x, y, cnt):
    global answer
    answer = max(cnt, answer)

    for i in range(4):
        nx = x + dx[i]
        ny = y + dy[i]

        # 범위 내에 있고, 방문안한 노드인지
        # arr[nx][ny]가 기존 알파벳들과 다른 알파벳인지
        if 0 <= nx < R and 0 <= ny < C and not visited[nx][ny]:
            if not checked[arr[nx][ny]]:
                visited[nx][ny] = 1
                checked[arr[nx][ny]] = 1
                DFS(nx, ny, cnt+1)
                visited[nx][ny] = 0
                checked[arr[nx][ny]] = 0
            

visited[0][0] = 1
checked[arr[0][0]] = 1
DFS(0, 0, 1)

print(answer)
문제링크

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

 

1655번: 가운데를 말해요

첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -1

www.acmicpc.net

문제설명

 

 

문제

백준이는 동생에게 "가운데를 말해요" 게임을 가르쳐주고 있다. 백준이가 정수를 하나씩 외칠때마다 동생은 지금까지 백준이가 말한 수 중에서 중간값을 말해야 한다. 만약, 그동안 백준이가 외친 수의 개수가 짝수개라면 중간에 있는 두 수 중에서 작은 수를 말해야 한다.

예를 들어 백준이가 동생에게 1, 5, 2, 10, -99, 7, 5를 순서대로 외쳤다고 하면, 동생은 1, 1, 2, 2, 2, 2, 5를 차례대로 말해야 한다. 백준이가 외치는 수가 주어졌을 때, 동생이 말해야 하는 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 백준이가 외치는 정수의 개수 N이 주어진다. N은 1보다 크거나 같고, 100,000보다 작거나 같은 자연수이다. 그 다음 N줄에 걸쳐서 백준이가 외치는 정수가 차례대로 주어진다. 정수는 -10,000보다 크거나 같고, 10,000보다 작거나 같다.

출력

한 줄에 하나씩 N줄에 걸쳐 백준이의 동생이 말해야 하는 수를 순서대로 출력한다.

예제 입력 1 

7
1
5
2
10
-99
7
5

예제 출력 1 

1
1
2
2
2
2
5​
 
문제풀이

시간초과난 풀이

시간초과가 날것으로 예상했는데 역시나였다.

 

 

해결방법

우선순위 큐를 이용하자!

두개의 힙을 만들어 하나는 최소힙, 다른 하나는 최대힙으로 구현하자.

 

핵심

leftHeap의 첫번째 원소에 중간값이 위치하게 만들면 된다.

rightHeap의 첫번째 원소가 leftHeap의 첫번째원소보다 크다면 중간값을 구할 수 없게 되므로

매 루프시 조건문을 통해 exchage하자!

 

이렇게 하면 매 루프시 sort를 시행하는 것보다 연산횟수 측면에서 단연 효율적이다.

코드
import sys
import heapq

input = sys.stdin.readline
N = int(input())
leftHeap = []
rightHeap = []

for i in range(N):
    data = int(input())

    if len(leftHeap) == len(rightHeap):
        # 최대힙으로 구현
        heapq.heappush(leftHeap, -data)
    else:
        # 최소힙으로 구현
        heapq.heappush(rightHeap, data)

    # leftHeap의 첫번째 원소를 중간값으로 만들기 위해 교환.
    if rightHeap and rightHeap[0] < -leftHeap[0]:
        l = heapq.heappop(leftHeap)
        r = heapq.heappop(rightHeap)

        heapq.heappush(rightHeap, -l)
        heapq.heappush(leftHeap, -r)        

    print(-leftHeap[0])
문제링크

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

 

1715번: 카드 정렬하기

정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장

www.acmicpc.net

문제설명

 

문제

정렬된 두 묶음의 숫자 카드가 있다고 하자. 각 묶음의 카드의 수를 A, B라 하면 보통 두 묶음을 합쳐서 하나로 만드는 데에는 A+B 번의 비교를 해야 한다. 이를테면, 20장의 숫자 카드 묶음과 30장의 숫자 카드 묶음을 합치려면 50번의 비교가 필요하다.

매우 많은 숫자 카드 묶음이 책상 위에 놓여 있다. 이들을 두 묶음씩 골라 서로 합쳐나간다면, 고르는 순서에 따라서 비교 횟수가 매우 달라진다. 예를 들어 10장, 20장, 40장의 묶음이 있다면 10장과 20장을 합친 뒤, 합친 30장 묶음과 40장을 합친다면 (10 + 20) + (30 + 40) = 100번의 비교가 필요하다. 그러나 10장과 40장을 합친 뒤, 합친 50장 묶음과 20장을 합친다면 (10 + 40) + (50 + 20) = 120 번의 비교가 필요하므로 덜 효율적인 방법이다.

N개의 숫자 카드 묶음의 각각의 크기가 주어질 때, 최소한 몇 번의 비교가 필요한지를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (1 ≤ N ≤ 100,000) 이어서 N개의 줄에 걸쳐 숫자 카드 묶음의 각각의 크기가 주어진다. 숫자 카드 묶음의 크기는 1,000보다 작거나 같은 양의 정수이다.

출력

첫째 줄에 최소 비교 횟수를 출력한다.

예제 입력 1 복사

3
10
20
40

예제 출력 1 복사

100
문제풀이

기본구조가 최소힙인 우선순위 큐 자료구조 활용하면 쉽게 풀리는 문제다.

 

힙에서 heappop() 시켜 작은수 먼저 더하여 answer배열에 저장하고,

힙의 마지막 두 수가 아니라면 더한값을 다시 heappush()하여 계속 이어나간다.

 

코드
import sys
import heapq

input = sys.stdin.readline
N = int(input())
heap = []
answer = []

for i in range(N):
    data = int(input())
    heapq.heappush(heap, data)

if N == 1:
    answer.append(0)
else:
    while True:
        a = heapq.heappop(heap)
        b = heapq.heappop(heap)
        answer.append(a+b)
        if not heap:
            break
        heapq.heappush(heap, a+b)

print(sum(answer))
문제링크

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

 

11286번: 절댓값 힙

첫째 줄에 연산의 개수 N(1≤N≤100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 0이 아니라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0

www.acmicpc.net

문제설명

 

문제

절댓값 힙은 다음과 같은 연산을 지원하는 자료구조이다.

  1. 배열에 정수 x (x ≠ 0)를 넣는다.
  2. 배열에서 절댓값이 가장 작은 값을 출력하고, 그 값을 배열에서 제거한다. 절댓값이 가장 작은 값이 여러개일 때는, 가장 작은 수를 출력하고, 그 값을 배열에서 제거한다.

프로그램은 처음에 비어있는 배열에서 시작하게 된다.

입력

첫째 줄에 연산의 개수 N(1≤N≤100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 0이 아니라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이라면 배열에서 절댓값이 가장 작은 값을 출력하고 그 값을 배열에서 제거하는 경우이다. 입력되는 정수는 -231보다 크고, 231보다 작다.

출력

입력에서 0이 주어진 회수만큼 답을 출력한다. 만약 배열이 비어 있는 경우인데 절댓값이 가장 작은 값을 출력하라고 한 경우에는 0을 출력하면 된다.

 

문제풀이

우선순위 큐

우선순위 큐는 큐의 형태를 띄고 있으나, 항상 정렬이 되어있다.

 

파이썬에서는 heapq라는 모듈을 사용해 이 우선순위 큐 자료구조를 구현할 수 있다.

PriorityQueue로도 구현할 수 있지만 thread-safe한 특성 때문에 heapq에 비해 상대적으로 느리다고 한다.

시간초과에 걸리지 않기 위해 코테에 사용하기에는 heapq가 좋겠다.

 

heapq의 기본구조는 최소힙

첫번째 요소는 항상 가장 작은 요소이기 때문에

heapq.heappop(리스트)시 최솟값이 나오게 되는 것이다.

 

절댓값 힙

튜플의 첫번째 요소에 절댓값을 넣고, 두번째 원소에 원래 수 를 넣는다.

절댓값이 같은경우, 두번째 요소 기준으로 다시 우선순위 매김. (오름차순)

코드
import heapq
import sys

input = sys.stdin.readline
heap = []
N = int(input())

for i in range(N):
    x = int(input())

    if x == 0:
        if heap:
            print(heapq.heappop(heap)[1])
        else:
            print(0)
    else:
        heapq.heappush(heap, (abs(x), x))
문제링크

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

 

14500번: 테트로미노

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

www.acmicpc.net

문제설명

 

문제

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.

  • 정사각형은 서로 겹치면 안 된다.
  • 도형은 모두 연결되어 있어야 한다.
  • 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다.

정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.

아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.

테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.

테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.

입력

첫째 줄에 종이의 세로 크기 N과 가로 크기 M이 주어진다. (4 ≤ N, M ≤ 500)

둘째 줄부터 N개의 줄에 종이에 쓰여 있는 수가 주어진다. i번째 줄의 j번째 수는 위에서부터 i번째 칸, 왼쪽에서부터 j번째 칸에 쓰여 있는 수이다. 입력으로 주어지는 수는 1,000을 넘지 않는 자연수이다.

출력

첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다.

예제 입력 1 

5 5
1 2 3 4 5
5 4 3 2 1
2 3 4 5 6
6 5 4 3 2
1 2 1 2 1

예제 출력 1 

19
문제풀이

DFS + 백트래킹으로 푸는 방법이 있다.

 

핵심

1. DFS를 시행하면서 백트래킹.

2. depth ==2 일때 예외처리  ==> ㅗ모양

 

 

주의점

이때 재귀함수를 활용하여 DFS를 하는데,

재귀함수에서는 인자로 전달할 변수의 값을 수정후 전달하지않도록 하자!

이렇게 되면 해당 값이 재귀의 각 레벨에 영향을 미치게 된다.

total 값을 전달할시

total += arr[nx][ny]

DFS(nx, ny, depth + 1, total) 

=> 이렇게 미리 변수값을 업데이트하고 인자로 넘기면 total값은 해당 재귀레벨에서 지속적으로 누적되게 된다.

 

따라서, 재귀 호출시에 인자에 누적과정을 같이 전달하는 방식을 사용하자!

 

코드
import sys

input = sys.stdin.readline
N, M = map(int, input().split())
arr = [list(map(int, input().split())) for _ in range(N)]
visited = [[0 for _ in range(M)] for _ in range(N)]
dx = [-1, 1, 0, 0] 
dy = [0, 0, -1, 1]
answer = 0

def isValid(r, c):
    if 0 <= r < N and 0 <= c < M:
        return True
    return False

def DFS(x, y, depth, total):
    global answer

    if depth == 4:
        answer = max(answer, total)
        return
    else:
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]

            if not isValid(nx, ny):
                continue
            if visited[nx][ny]:
                continue
            
            if depth == 2:
                visited[nx][ny] = 1
                DFS(x, y, depth+1, total+arr[nx][ny])
                visited[nx][ny] = 0

            visited[nx][ny] = 1
            DFS(nx, ny, depth+1, total+arr[nx][ny])
            visited[nx][ny] = 0


# for 문 돌려서 DFS 시행.
for i in range(N):
    for j in range(M):
        visited[i][j] = 1
        DFS(i, j, 1, arr[i][j])
        visited[i][j] = 0

print(answer)

+ Recent posts