[백준 17142번] 연구소 3 - Python으로 구현하기

 

문제 접근

  • BFS 문제이고, 역시나 itertools의 combinations로 조합의 수를 구해보자
  • 단, 문제에서 구해야 되는 건 그냥 dist가 아닌, 활성화바이러스가 아닌 칸의 dist 최대값으로 구해줘야한다.
  • 따라서 if map_data[nx][ny]==0: 의 조건안에 최대 dist를 구하는 부분으로 해결

 

생각해볼 점

  • list(sum(ar,[])) 대신 itertools의 chain 모듈을 쓰는게 훨씬 빠르다. Chain(*ar)

 

오답 분석

  • 비활성화 -> 활성화 되는 바이러스의 dist를 고려하는 실수가 많다.

 

from collections import deque
from itertools import combinations as cm

def bfs(virus_list):
    dist = [[-1] * N for _ in range(N)]
    dq=deque()
    for pos in virus_list:
        dq.append(pos)
        dist[pos[0]][pos[1]] = 0
    max_dist=0
    while dq:
        x,y=dq.popleft()
        for k in range(4):
            nx=x+di[k][0]
            ny=y+di[k][1]

            if 0<=nx<N and 0<=ny<N and map_data[nx][ny]!=1 and dist[nx][ny]==-1:
                dist[nx][ny]=dist[x][y]+1
                if map_data[nx][ny]==0:
                    max_dist=max(max_dist,dist[nx][ny])
                dq.append([nx,ny])

    a = list(sum(dist,[]))
    if a.count(-1)==list(sum(map_data,[])).count(1):	# 방문 안 한 곳이 벽의 개수와 동일한지
        ans.append(max_dist)	# 리스트없이 바로 min값 구해도 됨

N,M = map(int,input().split())
map_data = [list(map(int,input().split())) for _ in range(N)]
di = [[1,0],[0,1],[-1,0],[0,-1]]

virus_pos=deque()
ans=[]
for i in range(N):
    for j in range(N):
        if map_data[i][j]==2:
            virus_pos.append([i,j])

for now_virus_list in cm(virus_pos,M):
    bfs(now_virus_list)

print(min(ans) if ans else -1)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 15684번] 사다리 조작 - Python으로 구현하기

 

문제 접근

  • 주어진 사다리를 자료구조로 어떻게 표현해야 될지? 어떤 규칙이 있는지? 하나의 라인을 따라가보며 확인
  • 내가 생각한 사다리 자료구조는 배열이다.
  • itertools 의 product로 모든 인덱스 경우의 수를 구하고, combinations 로 조합을 뽑은 뒤 완전탐색을 해보자

[사다리 자료구조]

위 예시 사다리를 행렬로 표현해보면, 아래와 같다.(행은 가로선을 놓을 수 있는 곳, 열은 출발 라인번호)

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

 

array[1][1]은 1번->2번 가로선이 1번 가로선 놓을수 있는 곳에 위치해 있음을 뜻한다.

반복문으로 각 열마다 어떤 위치로 가는지 확인하여 i열이 i열로 끝나지 않으면 continue 하고, 다음 경우의수를 확인한다.

어떤 위치로 가는지 확인 하는 구체적인 방법은 아래 코드에서 확인!

 

생각해볼 점

  • 사다리 타기에 대한 로직을 한번 생각해 볼 수 있는 문제
  • product 모듈 안쓰고 인덱스 모든 경우의 수 찾기?(modular로 가능)
  • 최적화 고려해보기.

 

오답 분석

  • 시간초과가 많이 나는 문제이다.
  • 내 실수 : 반복문 안에 copy 모듈을 사용하여 처리를 깔끔하게 하려고 했으나, deepcopy의 시간복잡도가 O(n^2) 인것을 간과했다. 주의!!

 

* 2019-10-18 기준 백준 pypy3 채점서버에서 exit(0)을 런타임 에러로 인식하는 오류 있음.

from itertools import product,combinations as cm

def chk_sadari(sa,sa_added):
    for col in range(1,n+1):
        go = col
        for row in range(1,h+1):
            if sa[row][go]==1 or sa_added[row][go]==1:
                go+=1
            elif sa[row][go-1]==1 or sa_added[row][go-1]==1:
                go-=1
        if go != col:
            return False
    return True

n,m,h=map(int,input().split())
sadari = [[0]*(n+1) for _ in range(h+1)]    # 1 index

# 가로선이 없는 경우 예외처리
if m==0:
    print(0)
    exit(1)

# 사다리 입력정보
for _ in range(m):
    a,b=map(int,input().split())
    sadari[a][b]=1

if chk_sadari(sadari,sadari):
    print(0)
    exit(1)

for i in range(1,3+1):
    for now_line_list in cm(product(range(1,h+1),range(1,n)),i):  # 행렬의 인덱스를 1~h개를 뽑는 조합
        sadari_added = [[0]*(n+1) for _ in range(h+1)]
        # 뽑은 조합들로 사다리에 가로선 추가하기
        flag = False
        for pos in now_line_list:
            x,y=pos
            if sadari[x][y-1]==1 or sadari_added[x][y-1]==1 or sadari[x][y+1]==1 or sadari_added[x][y+1]==1 or sadari[x][y]==1:
                flag=False
                break
            sadari_added[x][y]=1
            flag= True
        if flag and chk_sadari(sadari,sadari_added):
            print(i)
            exit(1)
print(-1)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14891번] 톱니바퀴 - Python으로 구현하기

 

문제 접근

  • 주어진 문제는 구현 문제로 처음에 너무 어려워보였다. 쫄지말고 차근차근 접근하는 것이 필요.
  • 우선 톱니바퀴를 자료형으로 어떻게 표현 할 것인지, 주어진 조건에 따라 돌리는 경우, 안돌리는 경우를 어떻게 구현 할 것인지 부터 접근하자.
  • 톱니바퀴의 표현 : deque(index는 12시방향부터 시계방향으로 0~7을 가진다.)
  • 회전하는 경우 : 주어진 시작 톱니를 회전하고 왼쪽방향, 오른쪽방향 나누어서 돌린다. 도중에 맞닿은 부분이 같은 극인 경우 반복문 탈출
  • 참고 : 원형 돌리기 문제는 deque의 rotate 메소드를 쓰면 편하게 구현가능하다.

생각해볼 점

  • deque 와 list의 상황 별 시간복잡도가 어떻게 되는지 다시 한번 정리 필요
  • 재귀로 도전?

 

오답 분석

  • 1번, 4번 톱니가 시작인 경우 인덱스 처리를 잘못해주어 오류가 난 경우가 많아 보인다. 주의!

 

from collections import deque

circle = [deque(map(int,input())) for _ in range(4)]
opers = deque(list(map(int,input().split())) for _ in range(int(input())))

while opers:	# 명령어가 남아 있으면 반복문 실행
    num,direct = opers.popleft()
    num-=1  # 0 index
    tmp_2 = circle[num][2]	# 비교 대상 백업
    tmp_6 = circle[num][6]	# 비교 대상 백업
    circle[num].rotate(direct)	# 일단 시작 톱니 돌려주기
    tmp_direct=direct
    
    # 시작 톱니 왼쪽 방향
    direct=tmp_direct
    for i in range(num-1,0-1,-1):
        if circle[i][2]!=tmp_6:		# 서로 다른 극인 경우
            tmp_6=circle[i][6]
            circle[i].rotate(direct*-1)
            direct*=-1
        else:
            break

    # 시작 톱니 오른쪽 방향
    direct=tmp_direct
    for i in range(num+1,4):
        if circle[i][6]!=tmp_2:		# 서로 다른 극인 경우
            tmp_2 = circle[i][2]
            circle[i].rotate(direct*-1)
            direct*=-1
        else:
            break

ans=0
for i in range(4):
    if circle[i][0]==1:
        ans += (2**i)
print(ans)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14890번] 경사로 - Python으로 구현하기

 

문제 접근

  • 경사로를 놓을 수 있는 방법 : 높은 곳 -> 낮은 곳  / 낮은 곳 -> 높은 곳
  • 경사로가 겹치면 안되므로 경사로가 놓여진 부분은 따로 체크해주기
  • 2d에서 행과 열을 모두 확인해봐야 되는 문제이다. 행에 대한 구현만 완료한 뒤 2d 행렬을 트랜스포즈하여 열을 확인하자.
  • Python에서 2D array transpose : t_arr = list(zip(*arr))

 

생각해볼 점

  • [i][j] , [j][i] 의 순서로 이중 반복문 하나로 한번에 처리 가능하다 => 사실 트랜스포즈 과정이 필요없다.

 

 

오답 분석

  • 인덱스 표현에 주의하고 정확하게 하자! 한군데 틀리면 찾기 힘들다.
  • 인덱스를 다루는 문제일 수록 경계값 처리 꼼꼼하게 확인하기

 

def row_solve(arr):
    global cnt
    chk = [[False] * n for _ in range(n)]
    for i in range(n):
        j = 0
        go_flag = True
        while j < n - 1:
            if arr[i][j] == arr[i][j + 1]:
                j += 1
                continue
            elif arr[i][j] - arr[i][j + 1] == 1:  # 높->낮
                if arr[i][j + 1:j+1+L].count(arr[i][j+1]) == L:
                    chk[i][j + 1:j + 1 + L] = [True] * L
                    j = j + L
                    continue
                else:
                    go_flag = False
                    break
            elif arr[i][j] - arr[i][j + 1] == -1:  # 낮->높
                if arr[i][j+1 - L:j+1].count(arr[i][j]) == L and True not in chk[i][j+1 - L:j+1]:
                    chk[i][j+1 - L:j+1] = [True] * L
                    j += 1
                    continue
                else:
                    go_flag = False
                    break
            else:
                go_flag = False
                break

        if go_flag:
            cnt += 1

n,L=map(int,input().split())
map_data = [list(map(int,input().split())) for _ in range(n)]

cnt = 0
row_solve(map_data)
row_solve(list(zip(*map_data)))
print(cnt)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14889번] 스타트와 링크 - Python으로 구현하기

 

문제 접근

  • 두 팀으로 나누는 경우의 수를 어떻게 구할지?
  • itertools 의 combinations 모듈 사용! n//2 개의 조합을 뽑아서 1팀과 2팀으로 나눔

 

생각해볼 점

  • combinations 모듈 없이 어떻게 구현할지?
from itertools import combinations as cm
import math

n = int(input())
datas = [list(map(int,input().split())) for _ in range(n)]

min_ans=math.inf	# 정답

for case in cm(range(1,n+1),n//2):	# 두 팀으로 나눌 수 있는 경우의 수
    s1 = s2 = 0
    
    for i in case:	# 1팀 점수
        for j in case:
            s1+=datas[i-1][j-1]

    res = set(range(1, n + 1)) - set(case)	# 2팀 점수
    for i in res:
        for j in res:
            s2+=datas[i-1][j-1]
    min_ans=min(min_ans,abs(s1-s2))
print(min_ans)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14888번] 연산자 끼워넣기 - Python으로 구현하기

 

문제 접근

  • 주어진 연산자 개수내에 적절히 연산자를 조합하여 식 결과의 최댓값, 최솟값 찾기
  • itertools 의 permutations 모듈 사용하여 모든 경우의수 확인
  • set 을 사용하여 중복을 제거하자

 

생각해볼 점

  • 재귀 함수로 구현 가능해보임

 

오답 분석

  • 음수를 나누기 하는 경우 문제에 조건에 주어진 대로 처리

 

from itertools import permutations as pm

n = int(input())
nums = list(map(int,input().split()))
opers = list(map(int,input().split()))

# 입력받은 명령어 저장
op=''
for idx,oper in enumerate(opers):
    op+=str(idx)*oper

op=list(map(int,op))
res_list=[]
for now_op in set(pm(op,len(op))):	# 경우의 수(중복 제거)
    res = nums[0]
    for idx in range(n-1):
        if now_op[idx] == 0:
            res+= nums[idx + 1]
        elif now_op[idx] == 1:
            res-= nums[idx + 1]
        elif now_op[idx] == 2:
            res *=nums[idx + 1]
        elif now_op[idx] == 3:
            res = res//nums[idx + 1] if res>0 else ((res*-1)//nums[idx+1])*-1	# 음수 나누기 조심
    res_list.append(res)

print(max(res_list))
print(min(res_list))

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14503번] 로봇 청소기 - Python으로 구현하기

 

문제 접근

  • 정말 주어진대로 구현해야되는 구현 문제
  • 바라 보는 방향의 왼쪽을 표현하는 것이 관건
  • 입력으로 주어진 방향의 왼쪽방향은 -1 씩 감소되고, 반대편 방향은 2가 차이 난다.(0,1인경우는 예외처리)
  • 왼쪽방향 : n_dir = dir - 1 if dir>0 else 3 / 반대편 방향 : n_dir = dir - 2 if dir>1 else dir + 2

 

생각해볼 점

  • 문제를 코드에 옮기기 전, 대략적으로 구성도를 짜고 작성하기

 

오답 분석

  • 구현하면서 dir과 di가 헷갈리곤 했다. 변수 이름 잘 정해주기!

 

n,m=map(int,input().split())    # 맵 크기
r,c,dir = map(int,input().split())    # 로봇 r,c / 방향(0~3 : 북동남서)
map_data = [list(map(int,input().split())) for _ in range(n)]   # 빈칸0, 벽1
di = {0:[-1,0],1:[0,1],2:[1,0],3:[0,-1]}
while True:
    map_data[r][c] = 2  # 현재 위치 청소
    tmp_dir = dir
    chk_flag=False
    for _ in range(4):
        tmp_dir = dir-1 if dir>0 else 3 # 현재 보고있는 방향의 왼쪽부터 탐색
        nr=r+di[tmp_dir][0]
        nc=c+di[tmp_dir][1]
        if map_data[nr][nc]==0: # 왼쪽 방향에 청소 안한게 존재, 회전 후 한칸 전진

            dir=tmp_dir
            r,c=nr,nc
            chk_flag=True
            break
        else:
            dir=tmp_dir # 왼쪽 방향에 청소공간 없으면 회전

    if chk_flag:    # 왼쪽방향 청소안한게 있으면!
        continue

    tmp_dir = dir-2 if dir>1 else dir+2
    if map_data[r+di[tmp_dir][0]][c+di[tmp_dir][1]]==1:
        break
    else:
        r+=di[tmp_dir][0]
        c+=di[tmp_dir][1]

print(sum(map_data,[]).count(2))

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

[백준 14502번] 연구소 - Python으로 구현하기

 

문제 접근

  • 바이러스의 확산 => BFS 알고리즘
  • 벽을 세울 수 있는 경우의수 => itertools 모듈의 combinations, product
  • 안전영역 확인 => chain, counter 모듈
  • 메모리 효율성 문제로 제너레이터로 combinations 구현

 

생각해볼 점

  • itertools 모듈 안쓰고 경우의수 어떻게 뽑으면 좋을지?
  • copy 모듈을 이용하여 마지막 원상 복귀 코드 없애기

 

오답 분석

  • 항상 불필요한 로직이 없는지 확인하기

 

# 연구소
import sys
input = lambda:sys.stdin.readline().rstrip()

from itertools import product,chain,combinations as cm
from collections import deque,Counter

n,m=map(int,input().split())
a =[list(map(int,input().split())) for _ in range(n)]   # 맵

dq=deque()
di =[[-1,0],[0,-1],[1,0],[0,1]] # 4방향
max_cnt=0

for wall in cm(list(product(range(n),range(m))),3):
    return_wall=True
    for w in wall:
        x,y=w
        if a[x][y]==1 or a[x][y]==2:    # 벽을 둘 수 없는 자리
            return_wall=False
            break
    if not return_wall: continue    # 벽 3개를 설치 못한 경우
    else:
        for w in wall:  # 벽 설치
            x,y=w
            a[x][y]=1

    # 바이러스들 deque에 삽입
    for i in range(n):
        for j in range(m):
            if a[i][j]==2:
                dq.append([i,j])

    # bfs
    while dq:
        x,y=dq.popleft()
        for k in range(4):
            nx=x+di[k][0]
            ny=y+di[k][1]
            if 0<=nx<n and 0<=ny<m and a[nx][ny]==0:
                dq.append([nx,ny])
                a[nx][ny]=-1

    c=Counter(chain(*a))    # 2차원 맵 -> 1차원 맵
    max_cnt=max(max_cnt,c[0])   # 0의 개수

    # 기존 맵으로 원상복귀
    for w in wall:
        x,y=w
        a[x][y]=0
    for i in range(n):
        for j in range(m):
            if a[i][j]==-1:
                a[i][j]=0
print(max_cnt)

 

* 틀린 부분이나 더 나은 방법이 있을 수 있습니다. 지적해주시면 감사하겠습니다!!

+ Recent posts