취준! ✒/삼성

[코테준비] 삼성19. 삼성 코테 준비전에 꼭 볼 것- SW Expert에 대해서! (feat. 보호 필름 문제 python)

deepbluechip 2023. 10. 11. 02:03
728x90

SW Expert에 대해서

우선, 삼성코테를 보기 위해서는 해당 사이트에 익숙해질 필요가 있다고 한다. 삼성에서 운영하는 코딩테스트 사이트이다. 코드트리나 백준과는 또 다른 느낌이기에 꼭 체험해 보길 바란다.

https://swexpertacademy.com/main/main.do

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

이 사이트에 좋은 점은, 아래와 같다. 

  1. 삼성 코테의 input & output 형식에 대해 잘 알 수 있다. 아래 사진과 같이 삼성 코테는 여러 테케를 한 번에 돌리고 출력하는 것이다.  print(f'#{변수명}  {숫자}) ← 이것 사용에 익숙해질 필요가 있다.  & 초기화도
  2. 어떤 lib가 허용되는지, 안되는지 알 수 있다. 이 사이트에서 import sys를 하고 컴파일을 하면, 에러가 뜬다. sys를 못쓴다는 소문을 들었는데, 뭐가 더 안되는지 이렇게 import후에 컴파일해 보면 알 수 있다. (확실하지는 않지만, 높은 확률로 맞을 것이다)

정답 예시 (테케 10개)

참고로 ide를 사용할 수 있는데, python은 pycharm이다. pycharm으로 연습하는 습관도 들이면 좋을 것이다. 


익숙해지기 위해 문제를 하나 풀어봤다. 문제를 고른 방법은 '제출순'으로 나열했을 때, 별로 안 어렵고 & SW 모의 역량테스트 기출을 선택하였다. 

보호 필름

풀이

1. main코드를 짜며, 필요한 function 생각하기

for tt in t: # case개수만큼 
-- input 받기
-- if k ==1: ans =0
-- else: # 작업시작
---- ans = search() #필요한 작업수 찾기 function

2. "필요한 약물 작업 횟수 찾기 search()  "를 어떻게 할 것인가 → 재귀가 필요 (그냥 재귀 / bfs / dfs)

  • 그냥 생 상태로 test통과하는지 확인 / 투약 횟수를 1씩 늘리며 test통과하는지 확인 → test 통과하는지 확인하는 def필요 testpass(array)
    • 테스트 통과시, ans 업데이트 (현재가 더 작으면)
    • 테스트 미통과시, 약을 더 넣어봄 or 약을 더이상 못 넣으면 다음 단계로 넘어감
  • 어느 행에 약을 투여할지 정해야 함 → combination
    • for i in range(n) / for j in range(i+1, n) → 그전 loop이 어디서 시작했는지 알아야 함 → i 저장 필요 & 몇 개를 뽑는지 정해져 있지 않으므로 재귀 dfs
  • 모든 combination 다 해봐야 함 → 너무 오래걸림 → 중간에 아니다 싶으면 끊어줘야함
    • 아니다 싶은 경우: 
      • (1) 이미 최솟값보다 투약 횟수가 큰 경우 → 최솟값(정답값 ans)와 현재 투약횟수 비교필요
      • (2) k 보다 크거나 같은 경우 (k가 최댓값)  → k == 현재 투약횟수 → 더 이상 깊게 안들어가도 됨 (재귀함수), 즉, 그 케이스에 대해서 더 이상 투약 안해봐도 됨
  • 즉, (1) search 안에 (2) testpass (3) dfs 필요  → dfs가 search ! 2개 함수 정의 필요

3. dfs구성

뭘 업데이트하며 재귀해야하는가?

  1. 투약 횟수: pillcnt
  2. 어디서 부터 투약하는가: start

+ 바뀐 필름을 계속 새로 만들어서 재귀 하지 않고, film상태를 바꿔가며 재귀 (메모리 효율을 위해) 그래서 dfs를 나왔을 때는 다시 원상 복귀  어디를 바꿨는지 저장해둬야함 (이것은 각 재귀당 1열이니, 필름 전체를 만드는 것보다 효율적임. 1/d)

 

4. testpass 구성

  1. 열마다 체크
  2. 한 열이라도 k 이상이 아닌게 있으면 testpass fail - retrun False
  3. 중간에 retrun False안되었으면 마지막에 True

열마다 어떻게 체크?

  1. 지금과 다음 것 같은지 - 같으면 cnt +=1, 다르면 cnt =0
  2. 열 끝까지 안갔는데, cnt >= k 이면, 다음 열로 이동 (그 열은 성공)

+. 만약에 k ==1이면, 그냥 바로 투약안하고 통과 됨 - main코드에 넣어 놓기

5. 만약 중간에 안끊고, 다 찾는다면? 시간 복잡도

  • combination: d! = 13! > 10! = 3*10^6
  • test: w*d = 20*13 > 2*10^2
  • 대충 적어도 6*10^8 = 6억 이상 →  10억 넘어가면 안됨 →  위험

 

정답 코드 1💻

def inspect(film, k):
    # 열(w)마다 돌며, 각 행(d)의 끝까지 가야하므로, w가 밖, d가 안
    for i in range(w):
        # 한 막에 있는 셀 개수만큼
        stack =1 # 연속 셀 개수
        for j in range(d-1):
            # 막 개수 -1 만큼
            if film[j][i] == film[j+1][i]:
                #지금꺼랑 다음꺼랑 같으면,
                stack +=1
            else:
                # 다르면, 다시 세기 시작
                stack =1
            if stack ==k:
                # 기준 통과하면, 이 열은 더이상 안봐도 됨
                break
        # 한 열을 다 살펴봤는데, 개수가 k가 안되면, 불가능 한 것
        if stack !=k:
            return False
    # return False 안되었다는 것은, 다 True가 되었다는 것!
    return True

def dfs(l,s,film):
    """
    :param l: 약품 사용 횟수
    :param s: 약품 치는 곳 시작 위치 (0-1,2,3,4,5 / 1-2,3,4,5 / 2-3,4,5 / .. 이런식으로 combination
    :param film: 현재 film상태
    :return:
    """
    global answer
    if l>=answer:
        return
    if inspect(film, k): # 보호 필름 테스트 통과!
        if l<answer: # 현재 정답보다 투약횟수 적으면, 업데이트
            answer = l
        return
    # 테스트 통과 X
    if l==k: # 투약횟수가 최댓값이 되면,
        if l < answer: # 최댓값이 ans보다 작으면
            answer =l # 업데이트
        return
    else: # 투약횟수 아직 초과 X -> 투약 해줌
        # depth마다, 모든 열(w)을 바꿔줘야하니까, d가 밖 & w가 안
        for i in range(s,d):
            # (1) 1->0 0으로 다 바꿈
            switched = []
            for j in range(w):
                if film[i][j] == 1:
                    film[i][j] =0
                    switched.append(j) # 어디를 바꿨는지 기록
            # (2) 그 상태로 확인
            dfs(l+1,i+1, film)
            # (3) 다시 돌려놓음
            for j in switched:
                film[i][j] = 1
            switched=[]
            # (4) 1->0 . 1로 다 바꿈
            for j in range(w):
                if film[i][j] ==0:
                    film[i][j] =1
                    switched.append(j)
            # (5) 그 상태로 확인
            dfs(l+1, i+1, film)
            # (6) 다시 돌려 놓음
            for j in switched:
                film[i][j] =0



# main 문 ------
t = int(input())
for tt in  range(1, 1+t):
    d,w,k = map(int, input().split())
    films = [list(map(int, input().split())) for _ in range(d)] # d * w 배열
    answer = 1000000
    if k ==1:
        # 합격기준이 1이면 그냥 0임
        print(f'#{tt} {0}')
    else:
        dfs(0,0, films)
        print(f'#{tt} {answer}')

 

정답 코드 2💻

def passtest():
    """
    test 통과 여부
    :return: True / False
    """
    for j in range(w):
        cnt = 1
        for i in range(d-1):
            if film[i][j] == film[i+1][j]: # 지금과 다음이 같으면 cnt +=1
                cnt+=1
            else: # 아니면 초기화
                cnt=1
            if cnt==k: # 현재 k개 되면, 다음 열 확인
                break
        if cnt<k:
            return False # break 안되었으면, 통과 못했다이야기 - 한 열이라도 통과못하면 아웃
    return True # 다 break되었으면, 통과
        
def dfs(pillcnt, start):
    """
    :param pillcnt: 투약 횟수
    :param start: 약 어디서 부터 넣으면 되는지
    :return:
    """
    global answer, film
    # 이미 필요 없는 과정은 자르기
    if pillcnt >= answer: # 1) 현재 최솟값보다 작지 않은 경우
        return
    if pillcnt ==k: # 2) 최솟값의 최댓값보다 커지는 순간 - 무조건 통과함
        if answer>pillcnt:
            answer= pillcnt
        return
    # 다 검증을 할 필요도 없는 케이스를 다 했으니, 검증하기
    if passtest(): # test통과시
        if pillcnt<answer:
            answer=pillcnt
            return
    else: # 테스트 통과 못했으면, 더 투약하기 (더 깊게 들어가기)
        for i in range(start,d): # 앞에서 해본 경우는 안해봄
            # 깊게 들어갈 때, 투약을 먼저함 - film 변화 - 어떻게 변화하는지 저장 (메모리 효율)
            # [1] 약 ver1. 0->1
            #  1-1. 0->1 로 다 바꿈
            switched  =[] # 바뀐 부분 체크
            for j in range(w):
                if film[i][j] ==0:
                    switched .append(j)
                    film[i][j] =1
            # 1-2. 그 상태 check
            dfs(pillcnt+1, i+1)
            # 1-3. 다시 상태 돌려놓기
            for j in switched :
                film[i][j] =0

            # [2] 약 ver2. 1->0
            # 2-1. 1->0 로 다 바꿈
            switched =[]
            for j in range(w):
                if film[i][j] ==1:
                    switched.append(j)
                    film[i][j] =0
            # 2-2. 그 상태 check
            dfs(pillcnt+1, i+1)
            # 2-3. 다시 상태 돌려놓기
            for j in switched:
                film[i][j] =1
    return


import sys
sys.stdin = open("./input.txt")
input = sys.stdin.readline
# main & input
t= int(input())
for tt in range(t):
    answer = int(1e9) # 큰 값 넣어둠 (최솟값 찾는 것이므로)
    d,w,k = map(int, input().split())
    film = [list(map(int, input().split())) for _ in range(d)]
    if k ==1:
        print(f'#{tt+1} {0}')
    else:
        dfs(0,0)
        print(f'#{tt+1} {answer}')

정답!

728x90