문제링크

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

 

4963번: 섬의 개수

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다. 둘째 줄부터 h개 줄에는 지도

www.acmicpc.net

문제설명

문제

정사각형으로 이루어져 있는 섬과 바다 지도가 주어진다. 섬의 개수를 세는 프로그램을 작성하시오.

한 정사각형과 가로, 세로 또는 대각선으로 연결되어 있는 사각형은 걸어갈 수 있는 사각형이다. 

두 정사각형이 같은 섬에 있으려면, 한 정사각형에서 다른 정사각형으로 걸어서 갈 수 있는 경로가 있어야 한다. 지도는 바다로 둘러싸여 있으며, 지도 밖으로 나갈 수 없다.

입력

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다.

둘째 줄부터 h개 줄에는 지도가 주어진다. 1은 땅, 0은 바다이다.

입력의 마지막 줄에는 0이 두 개 주어진다.

출력

각 테스트 케이스에 대해서, 섬의 개수를 출력한다.

 
 
문제풀이
 

섬의 개수 즉, 구역 수가 몇개냐를 묻는 문제가 되겠다.

적록색약문제라든지, 유기농배추 문제라든지 여러 다른 BFS문제와 흡사한 방법으로 풀면 쉽게 해결된다.

 

다만,

1. 인접한 구역의 조건이 4방향이 아닌 대각선 방향까지 고려하여 8방향인 점

2. 테스트케이스의 개수가 주어지지 않고 0 0을 입력받으면 종료해야 하는점.

을 염두에 두자.  => 입력을 무한정 받다가, 조건에 부합하면 탈출할수 있도록 하자.  cf. [백준 1686] 복날

 

코드
# 섬의개수를 출력
# 1은 Land, 0은 바다
# BFS를 통해 구역수를 세는 문제 (같은 구역이 되려면 가로 혹은 세로 혹은 대각선 으로 인접해야함)
from collections import deque

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

def BFS(x, y):
    queue = deque([(x, y)])
    visited[x][y] = 1

    while queue:
        curX, curY = queue.popleft()

        for i in range(8):
            nx = curX + dx[i]
            ny = curY + dy[i]
            # 인덱스범위안에 있고 땅이고 방문하지 않았으면 방문
            if 0 <= nx < h and 0 <= ny < w and graph[nx][ny] == 1 and not visited[nx][ny]:
                queue.append((nx, ny))
                visited[nx][ny] = 1

while True:
    try:
        data = input()
        if data == '0 0' or not data:
            break
        w, h = map(int, data.split())
        graph = [[0 for _ in range(w)] for _ in range(h)]

        for i in range(h):
            graph[i] = list(map(int, input().split()))

        cnt = 0
        visited = [[0 for _ in range(w)] for _ in range(h)]
        # 구역 개수 구하기        
        for i in range(h):
            for j in range(w):    
                # 땅이고 방문하지 않은 노드면 방문.
                if graph[i][j] == 1 and not visited[i][j]:
                    BFS(i, j)
                    # 한번의 BFS끝날때마다 구역수 1증가
                    cnt += 1
        print(cnt)

    except:
        break
문제링크

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

 

16165번: 걸그룹 마스터 준석이

정우는 소문난 걸그룹 덕후이다. 정우의 친구 준석이도 걸그룹을 좋아하지만 이름을 잘 외우지 못한다는 문제가 있었다. 정우는 친구를 위해 걸그룹 개인과 팀의 이름을 검색하여 외우게 하는

www.acmicpc.net

문제설명

문제

정우는 소문난 걸그룹 덕후이다. 정우의 친구 준석이도 걸그룹을 좋아하지만 이름을 잘 외우지 못한다는 문제가 있었다. 정우는 친구를 위해 걸그룹 개인과 팀의 이름을 검색하여 외우게 하는 퀴즈 프로그램을 만들고자 한다.

입력

첫 번째 줄에는 총 입력 받을 걸그룹의 수 N(0 < N < 100)과 맞혀야 할 문제의 수 M(0 < M < 100)을 입력받는다.

두 번째 줄부터는 각 걸그룹마다 팀의 이름, 걸그룹의 인원 수, 멤버의 이름을 한 줄씩 차례대로 입력받는다. 팀과 멤버의 이름은 최대 100글자이며, 모든 글자는 알파벳 소문자이다. 하나의 걸그룹이나 서로 다른 두 걸그룹에 이름이 같은 두 멤버가 있는 경우는 없다.

그 다음 줄부터는 M개의 퀴즈를 입력받는다. 각각의 퀴즈는 두 줄로 이루어져 있으며, 팀의 이름이나 멤버의 이름이 첫 줄에 주어지고 퀴즈의 종류를 나타내는 0 또는 1이 두 번째 줄에 주어진다. 퀴즈의 종류가 0일 경우 팀의 이름이 주어지며, 1일 경우 멤버의 이름이 주어진다.

출력

첫 번째 줄부터 차례대로 퀴즈에 대한 답을 출력한다. 퀴즈의 종류가 0일 경우 해당 팀에 속한 멤버의 이름을 사전순으로 한 줄에 한 명씩 출력한다. 퀴즈의 종류가 1일 경우 해당 멤버가 속한 팀의 이름을 출력한다.

 
 
문제풀이

입력받기도 생각보다 까다롭고 조건이 까다로워서 생각보다는 헤맸던것 같다.

실제 코딩테스트에서 나온다면 시간안에 풀 수 있을지 의문이다...

우선 처음 시도했을 때의 생각은,

딕셔너리의 키: 벨류 쌍으로

# 형식 
# {
#     ('팀이름', 인원수): ['멤버1','멤버2', ...]
#   }


이런 형태로 집어넣으면 되겠거니 생각했다.

하지만 이것보다는 다음의 핵심을 활용하자.

 

★핵심

- 딕셔너리를 두개 사용하자 (왜 이 생각을 못했을까..)

코드
# N 걸그룹수, M 퀴즈수
N, M = map(int, input().split())
dict1 = {} # {'팀이름' : ['멤버이름','멤버이름', ...] }
dict2 = {} # 

# 걸그룹 데이터 전부 입력완료
for i in range(N):
    team = input()
    num = int(input())
    dict1[team] = []
    for _ in range(num):
        name = input()
        dict1[team].append(name)
        dict2[name] = team

for _ in range(M):
    quiz = input()
    type = int(input())

    # 타입이 1이라면 멤버의 소속팀 출력
    if type:
        print(dict2[quiz])
    # 0이라면 팀의 소속멤버 모두 출력
    else:
        memberArr = sorted(dict1[quiz])
        for member in memberArr:
            print(member)
문제링크

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

 

13414번: 수강신청

입력 데이터는 표준 입력을 사용한다. 입력은 1개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 과목의 수강 가능 인원 K(1 ≤ K ≤ 100,000)와 학생들이 버튼을 클릭한 순서를 기록한 대기목

www.acmicpc.net

문제설명

문제

국민대학교에서는 매 학기 시작 전 종합정보시스템에서 수강신청을 한다. 매 수강신청마다 아주 많은 학생들이 몰려 서버에 많은 부하가 가기 때문에, 국민대학교에서는 수강신청 부하 관리 시스템을 도입하기로 결정하였다. 새로운 관리 시스템은 다음과 같은 방식으로 동작한다.

  1. 수강신청 버튼이 활성화 된 후, 수강신청 버튼을 조금이라도 빨리 누른 학생이 대기목록에 먼저 들어간다.
  2. 이미 대기열에 들어가 있는 상태에서 다시 수강신청 버튼을 누를 경우 대기목록의 맨 뒤로 밀려난다.
  3. 잠시 후 수강신청 버튼이 비활성화 되면, 대기목록에서 가장 앞에 있는 학생부터 자동으로 수강신청이 완료되며, 수강 가능 인원이 꽉 찰 경우 나머지 대기목록은 무시하고 수강신청을 종료한다.

위의 표는 최대 수강 가능 인원이 3명인 알고리즘 수업에 대해 6명의 학생이 수강신청을 진행한 모습이다. 버튼이 비활성화 된 후, 먼저 규칙 1을 적용하여 클릭을 2번 이상 한 학생의 중복된 대기목록을 삭제한다. 중복된 목록을 제거한 후, 맨 앞에서부터 최대 수강 가능 인원인 3명을 선정한다. 표의 맨 오른쪽에는 그 최종결과를 나타낸 모습이다. 이와 같은 방법을 이용하여 최종적으로 수강신청에 성공한 인원을 출력하는 프로그램을 작성하시오.

입력

입력 데이터는 표준 입력을 사용한다. 입력은 1개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 과목의 수강 가능 인원 K(1 ≤ K ≤ 100,000)와 학생들이 버튼을 클릭한 순서를 기록한 대기목록의 길이 L(1 ≤ L ≤ 500,000)이 주어진다. 두 번째 줄부터 L개의 줄에는 수강신청을 버튼을 클릭한 학생의 학번이 클릭 순서대로 주어진다. 학번은 8자리의 숫자로 이루어져 있다.

출력

출력은 표준 출력을 사용한다. 입력받은 데이터에 대해, 수강신청 관리 시스템의 규칙을 적용한 후 수강신청에 성공한 인원의 학번을 한 줄에 1개씩 출력한다.

예제 입력 1 

3 8
20103324
20133221
20133221
20093778
20140101
01234567
20093778
20103325

예제 출력 1 

20103324
20133221
20140101

 

문제풀이

수강신청 부하 관리 시스템
  - 중복된 경우 대기번호 밀려난다. => 이전꺼 지우고 다시 삽입하는 것으로 구현하자!
  - 가장 앞에 있는 학생부터 수강 가능인원만큼 출력.

  - 하지만, K(수강 가능인원)가 db의 데이터 수보다 클 수 있다.

     => 이런 경우에는 K번 반복하며 인덱스 접근했다가 인덱스에러를 만나게된다.. 내가 그랬다.

     => 출력할때 K가 딕셔너리(리스트로 변환된) 의 길이 보다 클 경우와 작을경우 나눠서 출력하기.

 

처음엔 이 핵심들을 가지고 해시맵을 활용해서 구현하였다.

하지만 시간초과가 나와 sys.stdin.readline()을 이용하였는데 출력형식이 잘못되었다고 떠서,

rstrip()을 깜빡했단걸 깨달았다.

 

다시 기억하자...

input()으로 입력 받을땐 괜찮으나,

sys.stdin.readline() 이용하여 입력 받을경우 개행문자도 같이 받는다는거

 

코드
# 수강신청 부하 관리 시스템
# - 중복된 경우 대기번호 밀려난다. => 이전꺼 지우고 다시 삽입하면 될듯?
# - 가장 앞에 있는 학생부터 수강 가능인원만큼 출력.
import sys

input = sys.stdin.readline
K, L = map(int, input().split())
db = {}

for _ in range(L):
    student = input().rstrip()

    # db에 학생데이터 없다면
    if student not in db:
        db[student] = 1
    # 있다면 (버튼을 한번 더 누른 것이므로 맨뒤로 밀려남)
    else:
        del db[student]
        db[student] = 1

dbArr = list(db)
if len(dbArr) < K:
    for item in dbArr:
        print(item)
else:
    for i in range(K):
        print(dbArr[i])
문제링크

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

 

9375번: 패션왕 신해빈

첫 번째 테스트 케이스는 headgear에 해당하는 의상이 hat, turban이며 eyewear에 해당하는 의상이 sunglasses이므로   (hat), (turban), (sunglasses), (hat,sunglasses), (turban,sunglasses)로 총 5가지 이다.

www.acmicpc.net

문제설명

문제

해빈이는 패션에 매우 민감해서 한번 입었던 옷들의 조합을 절대 다시 입지 않는다. 예를 들어 오늘 해빈이가 안경, 코트, 상의, 신발을 입었다면, 다음날은 바지를 추가로 입거나 안경대신 렌즈를 착용하거나 해야한다. 해빈이가 가진 의상들이 주어졌을때 과연 해빈이는 알몸이 아닌 상태로 며칠동안 밖에 돌아다닐 수 있을까?

입력

첫째 줄에 테스트 케이스가 주어진다. 테스트 케이스는 최대 100이다.

  • 각 테스트 케이스의 첫째 줄에는 해빈이가 가진 의상의 수 n(0 ≤ n ≤ 30)이 주어진다.
  • 다음 n개에는 해빈이가 가진 의상의 이름과 의상의 종류가 공백으로 구분되어 주어진다. 같은 종류의 의상은 하나만 입을 수 있다.

모든 문자열은 1이상 20이하의 알파벳 소문자로 이루어져있으며 같은 이름을 가진 의상은 존재하지 않는다.

출력

각 테스트 케이스에 대해 해빈이가 알몸이 아닌 상태로 의상을 입을 수 있는 경우를 출력하시오.

예제 입력 1 복사

2
3
hat headgear
sunglasses eyewear
turban headgear
3
mask face
sunglasses face
makeup face

예제 출력 1 복사

5
3

 

문제풀이

 

코드
T = int(input())

for _ in range(T):
    n = int(input())
    dict = {}
    
    for i in range(n):
        wear = list(input().split())
        # 기존에 있었다면 리스트에 추가
        if wear[1] in dict:
            dict[wear[1]].append(wear[0])
        # 없던 옷종류라면 리스트 생성하여 옷 삽입
        else:
            dict[wear[1]] = [wear[0]]
    
    # 알몸만 아니면 되므로
    # 1종류 이상은 착용해야 한다.
    # 3종류(a, b, c) 가 있다면 착용가능한 조합은
    # (a개수+1) * (b개수+1) * (c개수+1)
    cnt = 1
    for key in dict:
        # 각 키에 해당하는값(배열이므로)의 길이
        cnt *= (len(dict[key]) + 1)
    
    # 집합 {a, b, c}로 나올 수 있는 경우
    # 공집합, {a}, {b}, {c}, {a,b}, {b,c}, {c,a}, {a,b,c}
    # 즉, 아무옷도 입지않은 경우를 빼준다.
    print(cnt - 1)
문제링크

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

 

1302번: 베스트셀러

첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고

www.acmicpc.net

문제설명

문제

김형택은 탑문고의 직원이다. 김형택은 계산대에서 계산을 하는 직원이다. 김형택은 그날 근무가 끝난 후에, 오늘 판매한 책의 제목을 보면서 가장 많이 팔린 책의 제목을 칠판에 써놓는 일도 같이 하고 있다.

오늘 하루 동안 팔린 책의 제목이 입력으로 들어왔을 때, 가장 많이 팔린 책의 제목을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고, 알파벳 소문자로만 이루어져 있다.

출력

첫째 줄에 가장 많이 팔린 책의 제목을 출력한다. 만약 가장 많이 팔린 책이 여러 개일 경우에는 사전 순으로 가장 앞서는 제목을 출력한다.

예제 입력 1 복사

5
top
top
top
top
kimtop

예제 출력 1 복사

top

 

문제풀이

리트코드의 twoSum 문제에서 해시테이블로 푸는걸 보고

해시맵 풀이를 익혔던것 같다.

 

핵심

1. 파이썬의 딕셔너리를 활용해서 해시맵 구현.

2. 조건에 맞게 삽입, 삭제

★3. 조건에 맞게 정렬 및 조회

    sorted() 를 쓸지, 리스트.sort() 를 쓸지 상황에 따라 판단.

   여기서는 딕셔너리를 리스트로 변환해

   리스트 내장함수 .sort()를 사용해 보았다.   

코드
N = int(input())

dict = {}

for _ in range(N):
    book = input()
    if book not in dict:
        dict[book] = 1
    else:
        dict[book] += 1

dictArr = list(dict.items())

dictArr.sort(key=lambda x: (-x[1], x[0]))

print(dictArr[0][0])
문제링크

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

문제설명

문제

상근이는 세계적인 소프트웨어 회사 기글에서 일한다. 이 회사의 가장 큰 특징은 자유로운 출퇴근 시간이다. 따라서, 직원들은 반드시 9시부터 6시까지 회사에 있지 않아도 된다.

각 직원은 자기가 원할 때 출근할 수 있고, 아무때나 퇴근할 수 있다.

상근이는 모든 사람의 출입카드 시스템의 로그를 가지고 있다. 이 로그는 어떤 사람이 회사에 들어왔는지, 나갔는지가 기록되어져 있다. 로그가 주어졌을 때, 현재 회사에 있는 모든 사람을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 로그에 기록된 출입 기록의 수 n이 주어진다. (2 ≤ n ≤ 106) 다음 n개의 줄에는 출입 기록이 순서대로 주어지며, 각 사람의 이름이 주어지고 "enter"나 "leave"가 주어진다. "enter"인 경우는 출근, "leave"인 경우는 퇴근이다.

회사에는 동명이인이 없으며, 대소문자가 다른 경우에는 다른 이름이다. 사람들의 이름은 알파벳 대소문자로 구성된 5글자 이하의 문자열이다.

출력

현재 회사에 있는 사람의 이름을 사전 순의 역순으로 한 줄에 한 명씩 출력한다.

예제 입력 1 

4
Baha enter
Askar enter
Baha leave
Artem enter

예제 출력 1 

Askar
Artem
문제풀이

해시테이블로 쉽게 풀 수있다.

하지만 sorted()의 인자로 들어갈 수 있는 혹은 설정할 수 있는 것들에 대한 공부가 미흡한 것 같다.

리스트.sort() 메서드랑 헷갈리기도 하고...

 

코드
import sys
input = sys.stdin.readline
N = int(input())
dict = {}
# enter 삽입 , leave 삭제로 해석해서 해시테이블 구현하자

# 입력값 받아서 딕셔너리 아이템 삽입, 삭제
for _ in range(N):
    item = list(input().split())
    
    if item[1] == 'enter':
        dict[item[0]] = 1
    elif item[1] == 'leave':
        del dict[item[0]]

dictArr = sorted(dict.keys(), reverse=True)

for person in dictArr:
    print(person)
문제링크

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

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net

문제설명

문제

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로로 이동하려 한다. 최단경로는 맵에서 가장 적은 개수의 칸을 지나는 경로를 말하는데, 이때 시작하는 칸과 끝나는 칸도 포함해서 센다.

만약에 이동하는 도중에 한 개의 벽을 부수고 이동하는 것이 좀 더 경로가 짧아진다면, 벽을 한 개 까지 부수고 이동하여도 된다.

한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

맵이 주어졌을 때, 최단 경로를 구해 내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 1,000), M(1 ≤ M ≤ 1,000)이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다. (1, 1)과 (N, M)은 항상 0이라고 가정하자.

출력

첫째 줄에 최단 거리를 출력한다. 불가능할 때는 -1을 출력한다.

예제 입력 1 복사

6 4
0100
1110
1000
0000
0111
0000

예제 출력 1 복사

15

 

문제풀이

목표지점까지 BFS를 통해 이동하면서 갈수 있는 최단거리를 구하는 게 목표다.

한칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸.

 

틀린풀이 (처음 시도한 생각)

0. 시작위치 큐에 삽입.

    deque([(0, 0, 0, 1)]) =>  즉 큐를 생성하면서 (x, y, crush, cnt)를 삽입한것이다.

    x, y는 시작좌표, crush는 벽을 부쉈는지 체킹하는 변수, cnt 는 출발지로부터의 거리값 갱신하는 변수.

1. 큐에서 현재좌표 뽑아내서 현재위치에서 갈 수 있는 다음위치를 계산 (상하좌우).

2. 다음위치에 있는 노드 방문가능한지 조건문 로직

    - 조건문은 1차적으로 인덱스 범위에 있는지, 그리고 방문하지 않은 노드인지 체크

    - 1차적으로 통과하면 노드가 (1) 길인 경우와 (2) 벽인 경우를 나누어 조건문 검사.

        - 길인 경우에 거리값 갱신 및 큐에 다음위치와 갱신한 거리값 삽입  => append((nx, ny, crush, cnt + 1))

        - 벽인 경우에,

               - 부순적이 없다면 큐에 (다음 위치, 거리값 갱신한 변수, 부쉈는지 여부 판별하는 변수) 삽입.

                  => append((nx, ny, crush + 1, cnt + 1))

 

이렇게 순조로울 거라고 생각했으나 18%에서 틀리고 말았다.

틀린 코드

import sys
from collections import deque

input = sys.stdin.readline
N, M = map(int, input().split())
# N 행 , M 열
graph = [[0 for _ in range(M)] for _ in range(N)]
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
visited = [[0 for _ in range(M)] for _ in range(N)]
cnt = 0
possible = False
answer = []

# 입력값 받아서 맵 채우기
for i in range(N):
    graph[i] = list(map(int, input().rstrip()))

def BFS(x, y):
    global cnt, possible
    queue = deque([(x, y, 0, 1)])

    while queue:
        curX, curY, crush, cnt = queue.popleft()

        # 목표 좌표에 도달했다면 서치 종료
        if curX == N-1 and curY == M-1:
            possible = True
            answer.append(cnt)

        for i in range(4):
            nx = curX + dx[i]
            ny = curY + dy[i]
      
            # 인덱스 범위에 있고, 방문하지 않은 노드인지 체크 
            if 0 <= nx < N and 0 <= ny < M and not visited[nx][ny]:
                # 노드가 0이면 방문
                if graph[nx][ny] == 0:
                    queue.append((nx, ny, crush, cnt + 1))
                    visited[nx][ny] = 1
                # 다음 이동할 좌표가 벽이라면
                else:    
                    # crush값이 0 이면(그전에 벽 부수지 않았다면)
                    if not crush:
                        queue.append((nx, ny, crush + 1, cnt + 1))
                        visited[nx][ny] = 1
                        
    return possible

if BFS(0, 0):
    print(min(answer))
else:
    print(-1)

 

올바른 풀이

도저히 모르겠어서 서치해보니 역시나 예외인 테케가 있었다.

노드를 방문했었던 것이,

기존에 벽을 부순적이 있는 상태에서 방문한건지, 

부순적이 없는 상태에서 방문한건지 알아야 할 필요가 있다.

 

즉, 무슨말이냐 현재위치까지 오면서 벽을 부쉈는지 여부에 따라 visited 저장을 다르게 해야한다.

visited리스트를 기존과 다르게 visited[check][nx][ny]로 바꾸어 구현하자.

 

코드

정답코드

import sys
from collections import deque

input = sys.stdin.readline
N, M = map(int, input().split())
# N 행 , M 열
graph = [[0 for _ in range(M)] for _ in range(N)]
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
visited = [[[0]*2 for _ in range(M)] for _ in range(N)]
possible = False
answer = []

# 입력값 받아서 맵 채우기
for i in range(N):
    graph[i] = list(map(int, input().rstrip()))

def BFS(x, y):
    global possible
    queue = deque([(x, y, 0, 1)])

    while queue:
        curX, curY, crush, cnt = queue.popleft()

        # 목표 좌표에 도달했다면 서치 종료
        if curX == N-1 and curY == M-1:
            possible = True
            answer.append(cnt)

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

            # 인덱스 범위 체크 
            if 0 <= nx < N and 0 <= ny < M:
                # 방문안한 노드라면 
                if not visited[nx][ny][crush]:
                    visited[nx][ny][crush] = 1
                    # 다음 이동할 노드가 길이라면
                    if graph[nx][ny] == 0:
                        queue.append((nx, ny, crush, cnt + 1))
                    # 다음 이동할 좌표가 벽이라면
                    else:
                        # 벽인데 그전에 다른벽을 부순적이 없다면
                        if not crush:
                            queue.append((nx, ny, crush + 1, cnt + 1))
                        
            # 벽인지 길인지 체크가 먼저냐
            # 아님 방문여부, 인덱스범위 체킹이 먼저냐 생각해보기

            # 일단 인덱스범위는 1차적으로 체킹해야될듯
            # => 잘못된 범위 참조해서 인덱스 오류날 수 있음
    return possible

if BFS(0, 0):
    print(min(answer))
else:
    print(-1)
문제링크

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

 

1686번: 복날

오늘은 복날이다! 해빈이는 복날을 맞아 순살치킨 파티를 하기 위해 닭을 잡으려고 한다. 하지만 이번에도 쉽게 잡힐까보냐. 용감한 닭 한 마리가 해빈이에게서 도망치려 한다. 닭은 v m/sec의

www.acmicpc.net

문제설명

문제

오늘은 복날이다!

해빈이는 복날을 맞아 순살치킨 파티를 하기 위해 닭을 잡으려고 한다. 하지만 이번에도 쉽게 잡힐까보냐. 용감한 닭 한 마리가 해빈이에게서 도망치려 한다. 닭은 v m/sec의 속도로 이동할 수 있고, (xs, ys)에서 (xt, yt)에 존재하는 벙커까지 이동하면 해빈이에게서 도망칠 수 있다. 하지만 닭이 m분 이상 벙커 밖을 돌아다닌다면 바로 해빈이에게 잡혀 치킨이 되고 말 것이다.

과연 닭은 해빈이에게서 도망칠 수 있을까?

입력

첫 번째 줄에는 닭의 속도 v와 생존 가능 시간 m이 주어진다. 두 번째 줄에는 닭의 시작 위치 xs와 ys가 공백을 사이에 두고 주어지며, 세 번째 줄에는 목적지의 위치 xt, yt가 주어진다.

그 다음부터는 중간 지점에 존재하는 벙커들의 좌표 x y가 주어진다.

모든 단위는 m(미터, meter)이다. 중간 지점에 존재하는 벙커들의 개수는 1,000개 이하이고, 모든 좌표의 범위는 -10,000에서 +10,000이다.

출력

만약 닭이 해빈이에게서 도망치는데 성공한다면, 출력은 "Yes, visiting n other holes."이라고 해야 한다(이때 n은 거쳐야 하는 중간 지점 벙커의 최소 개수이다). 한편 닭의 미래가 처참하다면 "No."를 출력하면 된다.

예제 입력 1 복사

3 1
0.000 0.000
500.000 0.000
179.000 0.000
358.000 0.000

예제 출력 1 복사

Yes, visiting 2 other holes.

 

문제풀이

어려웠다.

평면에서 두점 사이 거리 공식으로 조건 검사 하는 것 까지는 인지하였으나,

처음에 입력될 벙커 개수를 알려주지 않아, EOF 에러 처리 하는 것을 구현했다.

 

핵심은 BFS를 활용하는 거다.

BFS시,

노드 방문 조건문에 방문체킹하는 로직 구현 안해서 메모리 초과 나기도 했으므로 꼭 체킹하자.

 

핵심

1. v * m * 60 이상 이동하면 안됨.(조건문 처리) 벙커를 통과하면서 가는 것을 구현

2. 벙커좌표 따로 리스트에 담아두기. BFS시 전부 탐색해야하므로 bunker 리스트 전체 돌려서 조건문 검사할것

   cf. 조건문: 방문 여부 / 범위내로 이동가능한지

3. 방문체크하는 배열 따로 만들어서 방문처리하고 조건문에도 방문여부 체킹.

 

코드
import math
from collections import deque

v, m = map(float, input().split())
xs, ys = map(float, input().split())
xt, yt = map(float, input().split())
bunker = []
cnt = 0 
maxRange = v * m * 60
result = False
visited = []

# 벙커 개수 안알려주는듯?
# EOF Error 잡기
while True:
    try:
        data = input()
        if not data:
            break
        x, y = map(float, data.split())
        bunker.append((x, y))    
    except:
        break
    

def calculateDist(a, b, x, y):
    return math.sqrt((x-a)**2 + (y-b)**2)

def BFS(xs, ys):
    global cnt, result
    queue = deque([(xs, ys, cnt)])
  
    while queue:
        curX, curY, cnt = queue.popleft()

        if calculateDist(curX, curY, xt, yt) < maxRange:
            result = True
            return result

        for item in bunker:
            xm, ym = item
            if item not in visited and calculateDist(curX, curY, xm, ym) < maxRange:
                queue.append((xm, ym, cnt+1))
                visited.append(item)

    return result

if BFS(xs, ys):
    print(f"Yes, visiting {cnt} other holes.")
else:
    print('No.')


# 이동하는 거리 구하기 (x, y) (a, b) 
# 평면에서의 두 점 사이 거리 = (a-x)**2 + (b-y)**2 

# 3 m/s 속도인데 제한시간 60s 안이면
# 179m 이상 갈 수 없다.
문제링크

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

 

2230번: 수 고르기

N개의 정수로 이루어진 수열 A[1], A[2], …, A[N]이 있다. 이 수열에서 두 수를 골랐을 때(같은 수일 수도 있다), 그 차이가 M 이상이면서 제일 작은 경우를 구하는 프로그램을 작성하시오. 예를 들어

www.acmicpc.net

문제설명

문제

N개의 정수로 이루어진 수열 A[1], A[2], …, A[N]이 있다. 이 수열에서 두 수를 골랐을 때(같은 수일 수도 있다), 그 차이가 M 이상이면서 제일 작은 경우를 구하는 프로그램을 작성하시오.

예를 들어 수열이 {1, 2, 3, 4, 5}라고 하자. 만약 M = 3일 경우, 1 4, 1 5, 2 5를 골랐을 때 그 차이가 M 이상이 된다. 이 중에서 차이가 가장 작은 경우는 1 4나 2 5를 골랐을 때의 3이 된다.

입력

첫째 줄에 두 정수 N, M이 주어진다. 다음 N개의 줄에는 차례로 A[1], A[2], …, A[N]이 주어진다.

출력

첫째 줄에 M 이상이면서 가장 작은 차이를 출력한다. 항상 차이가 M이상인 두 수를 고를 수 있다.

제한

  • 1 ≤ N ≤ 100,000
  • 0 ≤ M ≤ 2,000,000,000
  • 0 ≤ |A[i]| ≤ 1,000,000,000

예제 입력 1 복사

3 3
1
5
3

예제 출력 1 복사

4

 

문제풀이

투포인터 알고리즘

주어진 배열이 정렬된 상태여야 효율적이다.

시간복잡도는 최악의 경우에도 O(N)

두 개의 포인터가 항상 left <= right 인 상태로 돌기 때문에 두 포인터가 증가하는 연산은 최대로 잡아도 N번만 수행된다.

 

핵심

1. 배열 오름차순 정렬 (내장함수 sort() 이용가능)

2. 두 포인터 이동 조건문 생각하기

    두 개의 수를 비교하는데 두 수의 차이가 M보다만 크다면 그것보다 큰 차이가 나는 두 수는 안봐도 된다.

   => 이말이 무슨 말이냐?

        A = [1, 2, 5, 6, 10] 이 있고 M=3 이라 할때 , 일단 두개 포인터를 i = 0, j = 1으로 두자.

        (1) arr[1] - arr[0] = 1 < M 이므로 우측포인터 i 1증가

        (2) arr[2] - arr[0] = 4 >= M 이다. 

             조건문 통과했으므로 어짜피 우리는 통과한 값중 가장 작은 값을 구할 것이므로

             이제 j만 증가시키는 일은 없다. (오름차순 정렬된 상태이므로)

             -  i 만 증가하거나

             - 두 개의 포인터가 인접한 상태라면 i, j 둘다 증가 

 

정리

두 수의 차이 < M:

  => 우측 포인터 이동  (조건 미충족이므로)

두 수의 차이 >= M:

  => 포인터가 바로 연속된 위치에 있다면

          -> 좌측, 우측 포인터 이동

       포인터 차이가 2이상 이면

          -> 좌측 포인터만 이동

 

코드
import sys

input = sys.stdin.readline
N, M = map(int, input().split())
A = [0] * N

for i in range(N):
    A[i] = int(input())

A.sort()

i = 0
j = 1
cnt = 0
answer = []

# 투포인터 활용
while j < N:
    if A[j] - A[i] >= M:
        answer.append(A[j]-A[i])
        i += 1
        j += 1
        if j - i > 1:
            j -= 1
    # 차이가 M보다 작다면
    else:
        j += 1
    
print(min(answer))

# 차이가 M이상
# M이상인 것들 중 차이가 가장 작은 거 고르면됨.
문제링크

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

 

2531번: 회전 초밥

첫 번째 줄에는 회전 초밥 벨트에 놓인 접시의 수 N, 초밥의 가짓수 d, 연속해서 먹는 접시의 수 k, 쿠폰 번호 c가 각각 하나의 빈 칸을 사이에 두고 주어진다. 단, 2 ≤ N ≤ 30,000, 2 ≤ d ≤ 3,000, 2 ≤

www.acmicpc.net

문제설명

문제

회전 초밥 음식점에는 회전하는 벨트 위에 여러 가지 종류의 초밥이 접시에 담겨 놓여 있고, 손님은 이 중에서 자기가 좋아하는 초밥을 골라서 먹는다. 초밥의 종류를 번호로 표현할 때, 다음 그림은 회전 초밥 음식점의 벨트 상태의 예를 보여주고 있다. 벨트 위에는 같은 종류의 초밥이 둘 이상 있을 수 있다.

 

새로 문을 연 회전 초밥 음식점이 불경기로 영업이 어려워서, 다음과 같이 두 가지 행사를 통해서 매상을 올리고자 한다.

  1. 원래 회전 초밥은 손님이 마음대로 초밥을 고르고, 먹은 초밥만큼 식대를 계산하지만, 벨트의 임의의 한 위치부터 k개의 접시를 연속해서 먹을 경우 할인된 정액 가격으로 제공한다.
  2. 각 고객에게 초밥의 종류 하나가 쓰인 쿠폰을 발행하고, 1번 행사에 참가할 경우 이 쿠폰에 적혀진 종류의 초밥 하나를 추가로 무료로 제공한다. 만약 이 번호에 적혀진 초밥이 현재 벨트 위에 없을 경우, 요리사가 새로 만들어 손님에게 제공한다.

위 할인 행사에 참여하여 가능한 한 다양한 종류의 초밥을 먹으려고 한다. 위 그림의 예를 가지고 생각해보자. k=4이고, 30번 초밥을 쿠폰으로 받았다고 가정하자. 쿠폰을 고려하지 않으면 4가지 다른 초밥을 먹을 수 있는 경우는 (9, 7, 30, 2), (30, 2, 7, 9), (2, 7, 9, 25) 세 가지 경우가 있는데, 30번 초밥을 추가로 쿠폰으로 먹을 수 있으므로 (2, 7, 9, 25)를 고르면 5가지 종류의 초밥을 먹을 수 있다.

회전 초밥 음식점의 벨트 상태, 메뉴에 있는 초밥의 가짓수, 연속해서 먹는 접시의 개수, 쿠폰 번호가 주어졌을 때, 손님이 먹을 수 있는 초밥 가짓수의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에는 회전 초밥 벨트에 놓인 접시의 수 N, 초밥의 가짓수 d, 연속해서 먹는 접시의 수 k, 쿠폰 번호 c가 각각 하나의 빈 칸을 사이에 두고 주어진다. 단, 2 ≤ N ≤ 30,000, 2 ≤ d ≤ 3,000, 2 ≤ k ≤ 3,000 (k ≤ N), 1 ≤ c ≤ d이다. 두 번째 줄부터 N개의 줄에는 벨트의 한 위치부터 시작하여 회전 방향을 따라갈 때 초밥의 종류를 나타내는 1 이상 d 이하의 정수가 각 줄마다 하나씩 주어진다.

출력

주어진 회전 초밥 벨트에서 먹을 수 있는 초밥의 가짓수의 최댓값을 하나의 정수로 출력한다.

 

문제풀이

구하고자 하는건 먹을 수 있는 초밥 가짓수의 최댓값이다.

 

핵심

1. 초밥은 회전하며 돌고 돌으므로 리스트를 하나 더 만들어 시작과 끝을 잇자.

 => extend() 활용

2. 연속으로 먹어야 한다 =>  슬라이딩 윈도우

 ★윈도우를 이동시키면서 윈도우의 맨 왼쪽값을 빼고 맨 오른쪽 원소의 다음값을 넣으면서 탐색.

=> O(N) 시간복잡도로 연산 가능하다.

 

 

잘못된 접근

처음에 봤을 땐, 문제 이해를 잘 못해서 회전할 수 있다는 내용을 보고 큐를 떠올렸다.

체크 배열을 만들어서 하나씩 초밥을 pop시켜

해당 초밥번호의 체크 배열 인덱스 값을 0에서 1로 갱신하고,

체크하지 않은 초밥과 쿠폰번호가 아닌 번호만 고르자 로 생각함;

 

두번째 시도했을땐, 투포인터를 떠올렸다.

하지만 시간복잡도를 간과하고 구현하니 시간초과 났다.

 

"핵심"부분에 언급한 내용을 상기하며 올바르게 구현할 수 있었다.

 

코드
import sys

input = sys.stdin.readline
N, d, k, c = map(int, input().split())  # d는 초밥가짓수, k는 연속으로 먹는 접시수
arr = [int(input()) for _ in range(N)]  # 배열에 입력값 받기 (초밥번호)
dict = {}  # 초밥 개수 저장할 딕셔너리
maxValue = 0
l = 0
r = l + (k-1)

# 회전하므로 리스트 두개를 잇는다
# 7 9 7 30 2 7 9 25 / 7 9 7 30 2 7 9 25
arr.extend(arr)

# 4개 연속으로 먹을거니까, 쿠폰번호 초밥 받음
dict[c] = 1

# 초기 윈도우에서 초밥 종류 세자.
for i in range(l, r+1):
    if arr[i] not in dict:
        dict[arr[i]] = 1
    else:
        dict[arr[i]] += 1

# 먹은 초밥 가짓수 세기
maxValue = len(dict)

r += 1

# 슬라이딩 윈도우 적용
while r < len(arr):
    # 맨 왼쪽 초밥 제거
    dict[arr[l]] -= 1
    if dict[arr[l]] == 0:
        del dict[arr[l]]

    # 오른쪽 초밥 추가
    if arr[r] not in dict:
        dict[arr[r]] = 1 
    else:
        dict[arr[r]] += 1
    
    cnt = len(dict)
    maxValue = max(cnt, maxValue)

    l += 1
    r += 1    

print(maxValue)

#★=> 슬라이딩 윈도우를 활용하여 O(N)내에 해결가능하다.

+ Recent posts