Skip to content

Conversation

@zaqquum
Copy link
Collaborator

@zaqquum zaqquum commented Apr 12, 2025

푼 문제가 여러 개라면 아래 폼 형식 복사해서 넣어주시면 됩니다 :D

🌱WIL

이번 한 주의 소감을 작성해주세요!

  • 이번주 일요일 삼성코테 시험이 잡혀, 이번 일주일은 2024년도 기출과 백준만 공부해봤다. 삼성 문항 1번만 집중적으로 풀이하면서, 문제 유형의 시뮬레이션과 BFS/ DFS 문제을 어떻게 접근 해야 할지 일부 감이 잡힌 것 같다. 다만 이번 삼성 문항을 처음 접하면서 문제 풀이 + 정리까지 최소 5시간 넘게 걸리니 하루에 한 문제씩 밖에 못 풀었고 , 부족한 시험준비기간에 비해 너무 비효율적으로 공부한 감이 있다. 다음 코테는 언제 몰지 모르겠지만 이번 교훈을 기반으로 코테는 미리미리 효율적으로 준비하자.

🚀주간 목표 문제 수: 5개

푼 문제


백준 #10844. 쉬운 계단수: DP / 실버1

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. DP 테이블 정의 : 2차원 배열(1차도 가능)

    DP[ i : 1~N] [ j : 0 - 9] : 길이가 i 고, 뒷자리가 J 인 계단수의 개수

    i : 길이

    j : 마지막 숫자

  2. 초기값

    dp [1][0] = 0 # 0 으로 시작하는 계단수가 아님

    dp[1] [1 - 9] = 1

  3. 점화식

    Case 1 ) j 가 1~ 8일때

    dp[i][j] = d[i-1][j-1] +dp[i-1][j+1]

    Case 2 ) j == 0 일 때 → 십의 자리 숫자가 1만 가능

    dp[i][0] = dp[i-1][1]

    Case 3 ) j == 9 일때 → 십의 자리 숫자가 8일 때만 가능

    dp[i][9] = dp[i-1][8]

🚩제출한 코드

"""
https://www.acmicpc.net/problem/10844
"""
n = int(input())
# dp [총 길이 i ][마지막 자리수가 j] = 인 개수
Mod = 1000000000
# 1. 초기화
#(1) 0을 시작하는 수는 계단수가 아닌다 - dp[1][0]
# (2) N=1 인 계단수
dp = [[0]*10 for _ in range(n+1)]
dp[1][0] = 0 
for j in range(1,10):
    dp[1][j]= 1

# (3) 점화식 N>=2
for i in range(2,n+1):
    for j in range(10):
        if j== 0 : 
            dp[i][j] = dp[i-1][j+1]    
        elif j==9:
            dp[i][j] = dp[i-1][j-1]
        else : 
            dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1]

print(sum(dp[n])%Mod)

💡TIL

배운 점이 있다면 입력해주세요

##백준 #1562. 계단수: 비트마스킹/ 골드1
정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

🚩제출한 코드

"""
https://www.acmicpc.net/problem/1562
"""
N = int(input())
MOD = 1e9
# dp[N번째 수][마지막 수][방문한 수 bitmasking(0~1023)]
dp = [[[0]*1024 for _ in range(10)] for _ in range(N+1)]
 
for i in range(1, 10):
    dp[1][i][1<<i] = 1
 
# n = N번째 수
for n in range(2, N+1):
    # i = 마지막 방문 숫자가 i
    for i in range(10):
        # 0~9까지 모든 수를 방문해야 한다는 조건이 있으므로, 방문 여부를 bitmasking을 통해 저장해야 함.
        for bit in range(1024):
            if i == 0:
                dp[n][i][bit | (1<<i)] += dp[n-1][i+1][bit]
            elif i == 9:
                dp[n][i][bit | (1<<i)] += dp[n-1][i-1][bit]
            else:
                dp[n][i][bit | (1<<i)] += dp[n-1][i-1][bit] + dp[n-1][i+1][bit]
            
            dp[n][i][bit | (1<<i)] %= MOD
    
res = 0
for i in range(10):
    res += dp[N][i][2**10-1]
 
print(int(res%MOD))

💡TIL

배운 점이 있다면 입력해주세요

백준 #1783. 병든나이트: 그리디 / 실버3

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?
1 .세로 N = 1

→ 이동 불가

  1. 세로 N = 2

    → 2가지 방법 { (2) 위 1 , 오른쪽 2 & (3) 아래 1 , 오른쪽2 } 만 가능

    ⇒ 최대 4개 ( 가로 칸수 7개 이상 가정)

  2. 세로 N ≥3

    → 4가지 이동 방법 모두 사용 가능

    (1) 최대 칸수가 4 미만인 경우

    → 가로 길이 m 이 4 이하인 경우

    (3) 최대 칸수가 4 이상인 경우

    → result 는 m-2 임

🚩제출한 코드

"""
실버3
https://www.acmicpc.net/problem/1783

#문제 : 그래프탐색(DFS , BFS)
- NxM 의 왼쪽 아래 칸 시작 
- 4가지 방법으로 이동 
    (1) 2칸 위 , 1칸 오른쪽 
    (2) 1칸 위로 2칸 오른쪽
    (3) 1칸 아래로 , 2칸 오른쪽
    (4) 2칸 아래 , 1칸 오느ㅜㄹ쪽
- goal)  방문할 수 있는  "최대 칸 개수"  구하기 

# 조건 
- <이동횟수가 4번 이상 -> 이동 방법 모두 사용 
- < 4번 이하 - 제약 없음 


"""
n, m = map(int, input().split())

result = 0
# n이 1일 때 무조건 1
if n == 1:
  result = 1
# n이 2일 때
elif n == 2: 
  if m >= 1 and m <= 6: #m이 1~6일 때
    result = (m + 1) // 2 
  elif m >= 7: #7이상일 때
    result = 4
# n이 3 이상일 때
elif n >= 3: 
  if m <= 6: #m이 1~6일 때
    result = min(m, 4)
  elif m >= 7: #m이 7 이상일 때
    result = m - 2
print(result)

💡TIL

배운 점이 있다면 입력해주세요

백준 #1541. 잃어버린 괄호: 그리디 / 실버2

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

🚩제출한 코드

💡TIL

배운 점이 있다면 입력해주세요

  • 좌표 문제라고 무조건 DFS , BFS 문제는 아니다 !

    무턱대고 DFS, BFS 문제로 확신해서 접근하니 풀이 아이디어가 획일화 되고, 시야가 좁아진 것 같다. 이번 문제처럼 먼저 Testcase 동작 과정을 생각하고 , 풀이 유형에 대한 다양한 가능성을 열어두고 접근하자. 직관으로 풀이과정을 찍는 것이 아닌 논리로 해당 동작 과정을 psedo code 형태로 접근해서 해결하자.

백준 #2437. 저울: 그리디 / 골드2

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  • 기존 구간 : [ 0 , acc[i-1]]
  • 신규 구간 [weight[i] , acc[i]]
  1. 총 N 개의 무게추를 뽑을 경우 , 1~ K(= N개의 무게추의 총합)의 무게를 측정할 수 있다고 가정

  2. N+1 번째의 새로운 무게추 X 를 추가할 경우 ,
    측정할 수 없는 무게가 생기는 경우는

    기존 측정 가능 법위인 1K 와 새로 측정 가능한 범위 (1K) * X 사이의 빈공간이 생길 경우

  3. 빈공간이 생기는경우 :

    1~K 까지는 측정 가능 →다음 측정할 K+1 의 측정 불가할 경우, 빈공간 발생

    → K+1 보다 X가 더 클 경우 ⇒ K+1 은 측정할 수 없는 최소값이 됨

🚩제출한 코드

"""
https://www.acmicpc.net/problem/2437

# 문제
- 저울 N개의 조합의 합으로 구현할 수 없는 양의 최소값 구하기
- N<=1000개
- 1개 무게 >= 1,000,0000
유형 : dp 인줄 알았지만 greedy 라는데 
-최소값 


N개 의 무게추 중 
"""
#1. 입력 저울추 & 오름차순 정렬
N = int(input())
weights = sorted(list(map(int, input().split())))
target = 1 
for w in weights : 
  if target < w :
    break
  
  target += w
print(target)

💡TIL

배운 점이 있다면 입력해주세요
image

백준 #9996. 한국이 그리울땐 서버에 접속하지: 문자열 / 실버3

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

🚩제출한 코드

n = int(input())
pattern = input().split("*")
length = len(pattern[0]) + len(pattern[1])

for _ in range(n):
	file = input()
	if length > len(file):
		print("NE")
		
	else:
		if pattern[0] == file[:len(pattern[0])] and pattern[1] == file[-len(pattern[1]):]:
			print("DA")
		else:
			print("NE")

💡TIL

배운 점이 있다면 입력해주세요

코드트리 #2024010101. 고대문명 유적탐사: 시뮬레이션, 그래프/ lv15

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?
[전체 Flow]

  1. 전체 K 번 반복
    1. 모든 경우의 수

      중심 좌표 9가지 * 각도 3가지 (90/180/270) = 27가지 경우의 value 값 구하기
      
      1. 회전 (rotate)

      2. 회전한 field 에서 획득한 유물 수 반환 : get old

      3. 리스트에 저장

        [유물 가치 , 회전 각도 , 중심 열 ,중심 행 , 삭제할 유물 ㅊ위치]

    2. 해당 턴에서 가장 Best 한 상황 선택

      <조건>

      (1) 유물 가치 ≠ 0 이면 종료

      (2) 유물 가치 최대 → (3) 회전 각도 최소 (4) 중심좌표 열 최소 (5) 중심 좌표 행 최소

    3. Best 한 상황에서 “유물 가치” 와 필드 상황 저장

    4. fiedld 상황 유물 매꾸기

      • 열이 작은순 ,행이 큰 순의 위치 부터 우선순위로 “유물벽화” 기준으로 새로운 유물 할당 받음

🚩제출한 코드

# 중심 좌표 후보군 +
# 0. 입력 변수 입력 받기
K , M = map(int, input().split())
# 0-1. 초기 필드 값 받기

sfield = [list(map(int,input().split())) for _ in range(5) ]
wall = list(map(int, input().split()))
# 상하좌우 
dy = [-1,1,0,0]
dx = [0,0,-1,1]


# (1) 90/180/270 회전 함수 
def rotate(i,j, angle ,field) :
    old_33=[[i-1 , j-1], [i-1, j],[i-1,j+1],[i+1,j-1] , [i,j] ,[ i,j+1], [i+1,j-1],[i+1,j],[i+1,j+1]]
    ro_pos =[]
    if angle == "90":
        ro_pos  = [6,4,0,7,4,1,8,5,2]
    elif angle == "180":
        ro_pos  = [6,7,8,3,4,5,0,1,2]
    else:
        ro_pos  = [0,3,6,1,4,7,2,5,8]
    new_field = [row[:] for row in field]
    for p in range(0,9):

       old = field[old_33[p][0]][old_33[p][1]]
       new_field[old_33[ro_pos.index(p)][0]][old_33[ro_pos.index(p)][1]] = old
    return field

# 현 field 상황에서 가치 업데이트
def get_old (field):
    del_pos = []
    visited = [] # 방문 여부 
    oldest = [] # 각 start point에서 연결된 유물 위치 
    for i in range(5):
        for j in range(5):
            if [i,j] not in visited : 
                # start point와 같은 종류의 유물만 획득 가능
                q = []
                q.append([i,j])
                visited.append([i,j])
                while q : 
                    cy,cx = q.pop()
                    for d in range(4) :
                        ny , nx = cy + dy[d] , cx + dx[d] 
                        if 0 <= ny < 5 and 0 <= nx < 5 :
                            if [ny,nx] not in  visited and field[ny][nx] == field[cy][cx]:
                                q.append([ny,nx])
                                visited.append([ny,nx])
                                oldest.append([ny,nx])
                
                    # 획득 있으면 -> 유물 개수 + 위치 누적
                if len(oldest) >= 3 :  # 3개 이상 연결시 획득 가능
                    del_pos.extend(oldest)
                    # print(del_pos)
                    oldest.clear()

    return len(del_pos) , del_pos
# best 상황 선택
def cur_best(current_case):
    arr = sorted(current_case , key=lambda x : (-x[0] , x[1] , x[2], x[3]))
    return arr[0] 


answer = []

for k in range(K):
    current_case = [] # 27경우 [유물가지, 각도 , 열, 행 ,삭제 위치 ]
    # 9개의 중심좌표 후보군
    ro_sub = [90,180,270]
    center_sub= [[1,1], [1,2],[1,3],[2,1] ,[2,2] ,[2,3],[3,1],[3,2],[3,3]]
    for center_y  , center_x in center_sub :
        for ro_angle in ro_sub : 
            sub_field = rotate(center_y,center_x, ro_angle ,sfield) 
            value_sub , del_sub  = get_old (sub_field)
            current_case.append([value_sub, ro_angle,  center_y, center_x, sub_field ,del_sub])
            
    #3. 해당 턴에서 베스트 상황 1개 선택
    # value 가 없는 경우 -> 끝
    if len(current_case) <= 0 : 
        break 
    best_sit=cur_best(current_case)
    answer.append(best_sit[0])
    #4. fiedl 상황 업데이트
    # 유물 매꾸기 - sort로 삭제된 위치 정렬 후 wall(유물벽면) 수행
    arr = sorted(best_sit[-1] , key = lambda x : (x[0] , -x[1])) # 사라진 유물 위치 
    sfield = best_sit[-2]
    pointer = 0 
    for y,x in arr : 
        node = wall[pointer]
        sfield[y][x] = node 
        pointer= (pointer+1)%len(wall)
        
print(answer)

💡TIL

배운 점이 있다면 입력해주세요

코드트리 #2024020101. 미지의 공간탈출: 시뮬레이션, 그래프/ lv14

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. 3d의 시작 - 끝 , 2d의 시작, 끝 좌표 찾기

    • 3d 의 통로 좌표 = 2d 좌표 변환
  2. 3d 의 최단 거리 (1 차 목적지 달성을 위한 BFS 최단 거리)

  3. 2d 의 최단 거리 (최종 목적지 까지 BFS 최단 거리)

    • F개의 이상현상 V 또한 고려

🚩제출한 코드

def myprint_3d(arr3) :
    for arr in arr3:
        for lst in arr: 
            print(*lst)
        print()
    print()

def myprint_2d(arr):
    for lst in arr:
        print(*lst)
    print()

# sk_3d ,si_3d, sj_3d = find_3d_start() 
def find_3d_start(): # 3d 출발 지점 좌표
    for i in range(M) :
        for j in range(M) :
            if arr3[4][i][j]==2 : 
                return 4, i, j
            
def find_2d_end(): # 2d에서 최종 도착 지점
    for i in range(N):
        for j in range(N) :
            if arr[i][j] == 4 : 
                arr[i][j] = 0 
                return i , j

def find_3d_base(): #  전체 맵에서 3d 영역 시작(절대)좌표  - 좌측 상단
    for i in range(N) :
        for j in range(N) :
            if arr[i][j] ==3 :
                return i, j 

# 3d 차원 & 2d 시작 위치 (텔레포트) 좌표 반환
# 3d : 상대 좌표 -> 2d 전체 맵 절대 좌표
def find_3d_end_2d_start():
    #[1] 3차원 시작 좌표(base) 찾기 =3 등장 -> 좌측상단 
    bi , bj  = find_3d_base()

    #[2] 3차원 좌표에서 2d 차원 연결 좌표 찾기 (1차 목적지 in 3d )
    # i,j : 2d 전체 맵 기준 
    # si ,sj = 2d통로 = 3d 통로 : (ek,ei,ej)
    for i in range(bi , bi+M) :
        for j in range(bj, bj+M) :
            if arr[i][j]!=3 : # 3차원 위치 아니면  skip
                continue
            
            # 1차 출구 : 3d 차원에서 ->2d 로 텔레포트 하는 위치
            if arr[i][j+1] == 0 : # 우측에 3d - 2d 탈출구 (3차원 우측으로 1차 출구)
                return 0 , M-1 , (M-1)-(i-bi) , i, j+1  # ek(평면) = 0 동쪽 , ei = M-1 , ej = i , si = i , sj = j+1
            elif arr[i][j-1] ==0 : # 좌측 1차 출구
                 return 1, M-1, i-bi, i, j-1    # ek(평면)=1, ei=M-1, ej=i, si=i, sj=j+1
            elif arr[i+1][j]==0 : # 아래쪽 1차 출구
                return 2, M-1 , j-bj,  i+1 ,j # ek(평면)=2, ei=M-1, ej=i, si=i, sj=j+1
            elif arr[i-1][j] == 0 : # 위쪽에 1차 출구
                return 3 ,M-1 , (M-1)-(j-bj) , i-1 , j # ek(평면)=3, ei=M-1, ej=i, si=i, sj=j+1
    #여기까지 올릴 없지만 
    return -1 
            
from collections import deque 
# dist = bfs_3d(sk_3d ,si_3d, sj_3d ,ek_3d , ei_3d,ej_3d)

left_nxt = {0:2, 2:1, 1:3, 3:0}
right_nxt = {0:3, 2:0, 1:2, 3:1}

def bfs_3d(sk ,si, sj ,ek , ei,ej):
    q = deque()
    v = [[[0]*M for _ in range(M)] for k in range(5) ] # 방문 여부 + 최소 거리 

    q.append((sk , si, sj))
    v[sk][si][sj] = 1 

    while q : 
        ck ,ci , cj = q.popleft()
        
        # 목적지 도달
        if (ck,ci,cj) == (ek,ei, ej) : 
            return v[ck][ci][cj]


        # 4방향, 범위내/ 범위 밖 -> 다른 k 로 이동 처리 , 미방문
        for di,dj in ((-1,0) ,(1,0), (0,-1), (0,1)):
            ni , nj = ci+di , cj + dj
            # 범위 밖으로 이동- > 다른 k 평면으로 이동 
            if ni<0 : #(1) 위쪽 범위 밖 이탈
                if ck == 0 : nk , ni , nj = 4 , (M-1)-cj , M-1
                elif ck == 1 :  nk,ni , nj = 4 , cj , 0
                elif ck == 2 : nk , ni, nj = 4, M-1 , cj
                elif ck == 3 :  nk , ni ,nj = 4,0,(M-1)-cj
                elif ck == 4 : nk, ni, nj  = 3 , 0 ,(M-1)-cj
            elif ni >= M : #(2) 아래쪽으로 범위 이탈
                if ck == 4 : nk, ni, nj  = 2, 0, cj
                else :  continue
            elif nj < 0 : # (3) 왼쪽 범위 이탈
                if ck ==4 : nk , ni , nj = 1, 0 , ci
                else : 
                    nk, ni, nj  = left_nxt[ck], ci , M-1
            elif nj >= M : # (4) 오른쪽 범위 이탈
                if ck == 4 :  nk, ni, nj  = 0 , 0, (M-1)-ci
                else : 
                    nk, ni, nj  = right_nxt[ck] , ci, 0
            else : # (5) 범위 내
                nk = ck 
            # 미방문 , 조건 맞으면 
            if v[nk][ni][nj] == 0 and arr3[nk][ni][nj] == 0 : 
                q.append((nk,ni,nj))
                v[nk][ni][nj] = v[ck][ci][cj]+1
    # 여긴 경로 없음 
    return -1 
# dist = bfs_2d(v , dist ,si,sj, ei , ej )
def bfs_2d(v , dist ,si,sj, ei , ej ):
    q= deque()
    
    q.append((si,sj))
    v[si][sj] = dist 

    while q : 
        ci,cj = q.popleft()
        if (ci,cj) == (ei,ej):
            return v[ci][cj]
        # 네방향, 범위내, (미방문)/조건맞으면(길이고, v[ci][cj]+1<v[ni][nj]  - 최단 거리)
        for di,dj in ((-1,0),(1,0),(0,-1),(0,1)):
            ni,nj = ci+di, cj+dj
            if 0<=ni<N and 0<=nj<N and arr[ni][nj]==0 and v[ci][cj]+1<v[ni][nj]:
                q.append((ni,nj))
                v[ni][nj]=v[ci][cj]+1

    return -1 
##########
########
#1. 입력변수  - 공간 한변의 길이N , 시간의 벽 한 변의 길이M, 시간 이상 현상 개수 F 공백
N,M,F = map(int, input().split())
arr= [list(map(int, input().split())) for _ in range(N)]
arr3 = [ [list(map(int, input().split())) for _ in range(M)] for k in range(5) ]
wall = [ list(map(int, input().split())) for _ in range(F)] # 시간 이상 : 초기위치 (r,c) , 확산 방향 d,  확산 상수 v

# [1] 주요 위치 찾기 
# 3d 의 시작 , 3차원 끝(탈출) , 2차원 시작 , 2차원 끝 좌표 탐색 
sk_3d ,si_3d, sj_3d = find_3d_start() 
ei , ej = find_2d_end() # 3d 출구(1차 도착지점) 좌표
ek_3d , ei_3d,ej_3d , si,sj =find_3d_end_2d_start() # 3d 출구(1차 도착지점) 랑 2d 시작점 
# print(f"1111111111111111")
# print(sk_3d ,si_3d, sj_3d )
# print(ei , ej )
# print(ek_3d , ei_3d,ej_3d , si,sj)

#[2] 3차원 공간 탐색 : 시작 위치 -> 탈출 위치거리 탐색(BFS 최단거리)
dist = bfs_3d(sk_3d ,si_3d, sj_3d ,ek_3d , ei_3d,ej_3d)
# 동 서 남 북
di=[ 0, 0, 1,-1]
dj=[ 1,-1, 0, 0]
if dist !=  -1 : # 3d 탈출 불가능
    #[3] 2차원 탐색 준비 : 시간 이상 현상 처리해서 v 에 시간 표시 : BFS 확산시 v 배수보다 작으면 통과 표시
    # value: 이상 현상이 발생하는 time 
    v = [[401]*N for _ in range(N)]
    
    for wi, wj , wd ,wv in wall : # 이상 현상 초기 위치 , 확산 방향 , 확산 상수
        v[wi][wj]  = 1 
        for mul in range(1, N+1) :
            wi ,wj = wi+di[wd] , wj + dj[wd] # 다음 확산될 곳
            if 0<= wi <N and 0<=wj <N and arr[wi][wj] == 0 and (wi,wj)!=(ei,ej) : # wv 단위로 wd 방향으로 확산표시(출구가 아닌 경우)
                if v[wi][wj] > wv*mul : # 더 큰값일때만 갱신(겹칠 수 있음)
                    v[wi][wj] = wv*mul
            else : 
                break
    
    #[4] 2차원 시작 위치에서 BFS로 탈출구 탐색
    dist = bfs_2d(v , dist ,si,sj, ei , ej )
print(dist)

💡TIL

배운 점이 있다면 입력해주세요
어려운 문제일 수록 당황하지 말고 최대한 나눠서 생각하자 !

코드트리 #2024020101. 메두사와 전사들: 시뮬레이션, 그래프/ lv15

정리한 링크: (바로가기)

🚩플로우 (선택)

코드를 풀이할 때 적었던 플로우가 있나요?

  1. BFS 로 메듀사 최단경로 찾기

    • : 도로 따라 공원까지 - 상하좌우
  2. 메듀사의 이동 : 지정된 최단거리로 한칸 이동 (전사와 만날 경우 , 전사 삭제)

  3. 메듀사 시선 : 상하좌우 방향중 "가장 많이 stone 되는 방향" 선택

4, 전사의 이동 (한 칸씩 두번) : 메듀사 있는 경우 공격

🚩제출한 코드

"""
BFS , 구현 , 시뮬레이션
"""
#상 o, 우상,  우, 우하 , 하, 좌하, 좌, 좌상
di = [-1,-1,0,1,1,1,0,-1]
dj = [0,1,1,1,0,-1,-1,-1]
#디버깅 용
def myprint(arr):
  for lst in arr: 
    print(*lst) # 2행 리시트는 1열이 1 instacne
  print()

# [0] BFS 로 메듀사 최단경로  : 도로 따라 공원까지 - 상하좌우
# route = find_route(si,sj,ei,ej)
from collections import deque
def find_route(si,sj,ei,ej): 
  q = deque()
  v = [ [0]*N for _ in range(N)] # 방문 여부 & *직전 위치*

  q.append((si,sj))
  v[si][sj] = (si,sj) # 직전 위치 저장

  while q : 
    ci, cj = q.popleft()

    # 목적지 도착 -> 경로 저장
    if (ci,cj)==(ei,ej) :

      route = []
      ci,cj = v[ci][cj]
      while (ci,cj) !=(si,sj): # 출발지가 아니면 저장
        route.append((ci,cj))
        ci,cj = v[ci][cj]
      return route[::-1] #역순(메듀사 이동 start- > end  순서대로)
  
    # 4방향(상하좌우), 범위내 , 미방문 ,조건(==0)
    for di,dj in ((-1,0),(1,0),(0,-1),(0,1)):
      ni, nj = ci+di , cj+ dj
      if 0<= ni <N and 0<=nj <N and v[ni][nj]== 0 and arr[ni][nj] ==0:
        q.append((ni,nj))
        v[ni][nj] = (ci,cj) # 바로 이전 좌표 저장

  #여기까지 오면? : 목적지 못찾음
  return -1


#  mark_line(v,ni,nj,dr)
def mark_line(v,ci,cj,dr):  # dir 방향 직선으로 : M ~>1 "w ~2"

  while 0<= ci< N and 0 <= cj<N: # 범위 나가는걸 방지
    v[ci][cj] = 2 # 시각적 구분을 위해 2로 표기 = safe 존
    ci,cj = ci+di[dr],cj+dj[dr] # 다음 dr 방향 칸 이동 

  

def mark_safe(v,si ,sj ,dr,org_dr): # si ,sj : 전사 위치
  #[1] 직선방향표기
  ci,cj = si+di[dr], sj+dj[dr]

  mark_line(v,ci,cj,dr) # v에 dr방향으로 이동 가능 지역 표시
 
  #[2]전사가 바라보는 방향으로 한줄 식 표시 : 전사에게 가려져 이동 가능 지역
  ci,cj = si+di[org_dr],sj+dj[org_dr] # 이동한 svf poin 초기화
  
  while 0<=ci<N and 0<=cj<N:          # 범위내라면 계속 진행
    mark_line(v,ci,cj,dr)
    ci,cj = ci+di[org_dr],cj+dj[org_dr] # org_dir로 point(대각선) 위치 이동


# tv , tstone = make_stone(marr , mi , mj dr) >>>tv , tston

def make_stone (marr, mi,mj,dr):
  v = [[0]*N for _ in range(N)]
  cnt = 0 # stone된 병사 개수
  
#   myprint(marr)
  #[1] dr 방향으로 w(>0) 만날때 까지 1 표시 , 이후 2 표시
  ni, nj = mi + di[dr] , mj+dj[dr]
  
  while 0<=ni<N and 0<=nj<N : # 범위내리면서 계속 진행
    v[ni][nj] = 1
    
    if marr[ni][nj] > 0 : # 병사 w 만남
      cnt+=marr[ni][nj] # 해당 영역의 모든 w 석화
      ni, nj = ni + di[dr] , nj+dj[dr] # 다음 칸으로 이동
  
      mark_line(v,ni,nj,dr) # v에 dr 방향으로 이동 가능 지역 표시

      break
    ni, nj = ni + di[dr] , nj+dj[dr]
  #[2] dr -1 ,dr +1 방향으로 M의 시선 동일 처리, 대각선 원점 잡고 dr 방향으로 처리
  for org_dr in ((dr-1)%8 , (dr+1)%8):
    si,sj = mi+di[org_dr], mj+dj[org_dr] # 첫 대각선 위치부터 체크

    # 대각선 시야각 영역 확인
    while 0<=si <N and 0<=sj <N : # 대각선 방향으로 초기 위치 탐색 후 직선 시선
      if v[si][sj] == 0 and marr[si][sj]> 0: # 전사 만남
        v[si][sj] = 1 
        cnt += marr[si][sj]
        mark_safe(v,si,sj,dr,org_dr) # 전사가 바라보는 방향으로 safe(이동 가능 범위) 표시
        break
      # W 가 길 중간에 있는 경우
      ci ,cj = si,sj # 첫 위치가 전사가 아닐 경우는 직선으로
      while 0 <= ci <N and 0<= cj <N: # 범위 내리면서 계속 진행
        if v[ci][cj]==0: # 처음 방문
          v[ci][cj] = 1 
          if marr[ci][cj]>0 : # 전사로 막히면
            cnt+= marr[ci][cj]
            mark_safe(v, ci,cj,dr,org_dr) # v에서 dr 방향으로 이동 가능 지역 표시
            break
        else :  
          break
        ci,cj = ci+di[dr], cj+dj[dr]

      si,sj = si+di[org_dr], sj+dj[org_dr]
  return v, cnt

# move_cnt , attk_cnt = move_men(v,mi,mj)

def move_men(v,mi,mj) :
  # [3] 전사의 이동 - (상하좌우)(좌우상하) 메두사 시야 아니면 (!=1)
  move , attk = 0,0
  for dirs in (((-1,0),(1,0),(0,-1),(0,1)), ((0,-1),(0,1),(-1,0),(1,0))):
    for idx in range(len(men)-1,-1,-1): 
      ci ,cj = men[idx] 
      if v[ci][cj] == 1 : # 메듀사 시야 내에 있으면 -> 정지
        continue 
      dist = abs(mi-ci) +abs(mj-cj) # 현재 거리
      for di,dj in dirs : 
        ni,nj = ci +di ,cj +dj # w 의 다음 이동 공간
        #범위내 메듀사 시야 아니고, 현재 거리보다 더 줄어드는 방향 (상하좌우 우선순위로 이동)
        if 0<=ni<N and 0<=nj<N and v[ni][nj]!=1 and  dist>abs(mi-ni)+abs(mj-nj):
          if (ni,nj) == (mi,mj): # 공격 -> 죽음
            attk+=1 
            men.pop(idx) 
          else :  
            men[idx] = [ni,nj]
          move += 1
          break
  return move, attk

###############
# main FLOW
###############

    
# 0. 입력 
# 마을 크기 M , 전산수 N / 메듀사 위치정보s ,  공원 위치 정보 e /  M명 전사 좌표 N/ 개의 도로 정보 
N , M = map(int, input().split())
si , sj , ei , ej = map(int , input().split())
tlist = list(map(int , input().split()))

# 전사 좌표 
men = []
for i in range(0, M*2 , 2) :
  men.append([tlist[i], tlist[i+1]])
# 필드 정보
arr =[list(map(int, input().split())) for _ in range(N)]



# [0] BFS 로 메듀사 최단경로  : 도로 따라 공원까지 - 상하좌우

route = find_route(si,sj,ei,ej)



if route == -1 : # 길이 없는 경우
  print(-1)
else : 
  for mi,mj in route :  # 각 time = idx 별 메듀사 위치 
    move_cnt , attk_cnt = 0, 0
    # [1]메듀사의 이동 : 지정된 최단거리로 한칸 이동 (전사와 만날 경우 , 전사 삭제)
    
    for i in range(len(men)-1,-1,-1) : # 역순 탐색
      if men[i] == [mi,mj] :
        men.pop(i)

    #[2] 메듀사 시선 : 상하좌우 방향중 "가장 많이 stone 되는 방향" 선택
    # v[] : 메듀사의 시선 명시해서 이동시 참조(메듀사시선 =1 , 전사에게 가려진 곳 ==2 ,빈땅 ==0)
    # mar[][] : 지도에 있는 현재 전사 수 (및 위치) 표시(중복 존재 가능)
    marr = [[0]*N for _ in range(N)]
    for ti, tj in men : 
      marr[ti][tj] += 1
    

    max_stone = -1 # 4 방위 중 max stoning 가능한 개수
    v= [] #현 필드내 메듀사 시선으로 생기는 영억 속성 설정

    # 4방위 바교 
    for dr in (0,4,6,2) : # 상하좌우 순서대로 처리 
      tv , tstone = make_stone(marr , mi , mj, dr) 
      # 최대값 갱신 - max_stone 개수 & 시선 영역 속성 설정
      if max_stone < tstone : 
        max_stone = tstone
        v = tv

    


    #[3] 전사의 이동 (한 칸씩 두번) : 메듀사 있는 경우 공격
    # 메듀사와 가까워지는 방향으로 접근
    move_cnt , attk_cnt = move_men(v,mi,mj)
  
    print(move_cnt, max_stone , attk_cnt)
  print(0)

💡TIL

배운 점이 있다면 입력해주세요
복잡한 문제 ⇒ 단계별 구현 / 검증

Copy link
Collaborator

@Mingguriguri Mingguriguri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각각의 문제 번호들은 기본키라고 생각해주세요! 현재 아래 2개의 문제 번호가 겹치고 있는 것 같아요! 수정 부탁드립니다아

스크린샷 2025-04-14 오전 9 26 33
  • 일요일에 코테 시험은 잘 보셨나요?
    고생 많으셨습니다. 열심히 한 주를 보내신 것 같네요!
  • 코테는 미리미리 효율적으로 준비하는 거 정말 맞는 것 같아요..!!! 앞으로 저희 열심히 문제 풀이 해봐유!!
  • 그리고 코드리뷰하려고 과제 문제인 계단수를 찾아봤는데 커밋에 없는 것 같아요..!

Copy link
Member

@YoonYn9915 YoonYn9915 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

방문 여부를 bitmasking을 통해 저장해야 한다는게 새로웠습니다.
좋은 과제를 내 주셔서 연습에 도움이 된거 같아요! 한주 수고하셨습니다.

@zaqquum zaqquum merged commit 92da647 into main Apr 14, 2025
@github-actions
Copy link

🔥2025-04 챌린지 진행 상황

👉 그래프

  • YoonYn9915: 0개 ❌
  • Mingguriguri: 0개 ❌
  • zaqquum: 3개 ❌

👉 DP

  • YoonYn9915: 2개 ❌
  • Mingguriguri: 4개 ❌
  • zaqquum: 1개 ❌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants