KEEP GOING

[python code review] 6주차 (암호 만들기, 기둥과 보 설치, Z) 본문

code review/study

[python code review] 6주차 (암호 만들기, 기둥과 보 설치, Z)

jmHan 2022. 3. 11. 09:58
반응형

[암호만들기]

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

 

1759번: 암호 만들기

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

www.acmicpc.net

[bruteforce]

from itertools import combinations

l, c = map(int, input().split())
words = sorted(input().split())

for comb in list(combinations(words, l)):
    v, e = 0, 0
    for word in comb:
        if word in ['a', 'e', 'i', 'o', 'u']:
            v += 1
        else:
            e += 1
    # 최소 한개의 모음과 두개의 자음을 포함한다면
    if v >= 1 and e >=2:
        print(''.join(comb))

 

combinations을 이용하면 가장 간단해보여서 구현해봤더니 정답 처리되었다. 

자음이 영단어로 뭐였는지 기억이 안나서 e라고 했는데.. 자음은 영어로 Consonants이다.  

코드 개선 )

for comb in list(combinations(words, l)):
    v = 0
    for word in comb:
        if word in ['a', 'e', 'i', 'o', 'u']:
            v += 1
    # 최소 한개의 모음과 두개의 자음을 포함한다면 : 자음 변수를 만들지 않고 l-v 사용 
    if v >= 1 and (l-v) >= 2:
        print(''.join(comb))

 

그리고 딱 보니까 백트래킹으로도 가능해서 이리저리 열심히 풀어보았다.

[backtracking]

L, C = map(int, input().split())
words = sorted(input().split())

def possible(lst):
    vowel, consonants = 0, 0
    for data in lst:
        if data in ['a', 'e', 'i', 'o', 'u']:
            vowel += 1
        else:
            consonants += 1
    if vowel >= 1 and consonants >= 2:
        return True
    return False


def backtracking(l, idx, arr):
    if len(arr) == l:
        if possible(arr):
            print(''.join(arr))
        return
    for idx in range(idx, C):
        arr.append(words[idx])
        backtracking(l, idx + 1, arr)
        arr.pop()

backtracking(L, 0, [])

코드개선)

L, C = map(int, input().split())
words = sorted(input().split())
vowels = ['a', 'e', 'i', 'o', 'u']

def backtracking(l, idx, arr, v, c):
    if len(arr) == l:
        if v >= 1 and c >= 2:
            print(''.join(arr))
        return
    for idx in range(idx, C):
        arr.append(words[idx])
        if words[idx] in vowels:
            backtracking(l, idx + 1, arr, v+1, c)
        else:
            backtracking(l, idx + 1, arr, v, c+1)
        arr.pop()

backtracking(L, 0, [], 0, 0)

 

이런 식으로 모음개수와 자음개수를 매개변수로 이용하여 풀면 더 간결하게 문제를 풀 수 있다.

백트래킹이 멋은 있지만 구현하기 어려울 뿐더러 combination을 사용한 풀이가 속도면에서 더욱 빨랐다.

코딩테스트에서는 왠만하면 combination을 사용하여 정확하고 빠르게 풀이하는게 나을 듯 싶다.

 

 

[기둥과 보 설치]

https://programmers.co.kr/learn/courses/30/lessons/60061

 

코딩테스트 연습 - 기둥과 보 설치

5 [[1,0,0,1],[1,1,1,1],[2,1,0,1],[2,2,1,1],[5,0,0,1],[5,1,0,1],[4,2,1,1],[3,2,1,1]] [[1,0,0],[1,1,1],[2,1,0],[2,2,1],[3,2,1],[4,2,1],[5,0,0],[5,1,0]] 5 [[0,0,0,1],[2,0,0,1],[4,0,0,1],[0,1,1,1],[1,1,1,1],[2,1,1,1],[3,1,1,1],[2,0,0,0],[1,1,1,0],[2,2,0,1]] [[

programmers.co.kr

[구현]

def build(answer):
    for x, y, s in answer:
        # 기둥인 경우
        if s == 0:
            # 바닥 위에 있는 경우
            if y == 0:
                continue
            # 보의 한쪽 끝에 위에 있는 경우
            if [x, y, 1] in answer:
                continue
            if [x-1, y, 1] in answer:
                continue
            # 다른 기둥 위에 있는 경우
            # if [x, y+1, 0] in answer:
            if [x, y-1, 0] in answer:
                continue
            return False
        # 보인 경우 (s == 1)
        else:
            # 한쪽 끝 부분이 기둥 위에 있는 경우
            if [x, y-1, 0] in answer:
                continue
            if [x+1, y-1, 0] in answer:
                continue
            # 양쪽 끝 부분이 다른 보와 동시에 연결된 경우
            if [x-1, y, 1] in answer and [x+1, y, 1] in answer:
                continue
            return False
    return True
        
def solution(n, build_frame):
    answer = []
    for i, j, a, b in build_frame:
        # 삭제할 경우 
        if b == 0:
            answer.remove([i,j,a])
            if not build(answer):
                answer.append([i,j,a])
        # 설치할 경우 
        else:
            answer.append([i,j,a])
            if not build(answer):
                answer.remove([i,j,a])
        
    return sorted(answer)

처음에는 아래처럼 build 함수에서 answer 변수를 전역변수로 지정하여

우선 기둥인 경우와 보인 경우를 나눈 뒤 answer 리스트에 조건을 만족하는 변수가 있는 경우,

answer 리스트에 해당 변수를 추가하게끔 구현.

하지만 이런식으로 구현하려다보니 보의 조건 중에서 '양쪽 끝 부분이 다른 보와 동시에 연결된 경우'를 구현하기 위해서는 이중 for문이 필요하였기에 매우 비효율적이었다.

전에 이 문제를 풀어본 적이 있어서 모든 answer 변수와 target x, y 변수를 비교하지 말고 조건에 충족하는 좌표가 answer 리스트 있는지 즉 if 조건에 충족하는 좌표 in answer:  라는 조건문을 대체하여 쓸 수 있다는 점이 떠올랐다. 

조건 하나라도 실수하는 경우 오답이 나오는 구현 문제가 코딩테스트에서 많이 출제된다.    

 

def build(x, y, structure):
    global answer
    # 기둥인 경우
    if structure == 0:
        # 바닥 위에 있는 경우
        if y == 0:
            answer.append((x, y, structure))
            return
         
        for sx, sy, s in answer:
            # 보의 한쪽 끝에 위에 있는 경우
            if s == 1 and (sx = x or sx+1 == x):
                answer.append((x, y, structure))
                return
            # 다른 기둥 위에 있는 경우
            if s == 0 and (sy+1 == y):
                answer.append((x, y, structure))
                return

 

[Z]

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

 

1074번: Z

한수는 크기가 2N × 2N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다. N > 1인 경우, 배열을

www.acmicpc.net

[재귀 구현]

n, r, c = map(int, input().split())
result = 0
while n > 1:
    # 각 사분면의 길이
    half = (2**n)//2
    # 2사분면에 위치
    if r < half and c < half:
        pass
    # 1사분면에 위치
    elif r < half and c >= half:
        result += half**2
        c -= half
    # 3사분면에 위치
    elif r >= half and c < half:
        result += (half**2)*2
        r -= half
    # 4사분면에 위치
    else:
        result += (half**2)*3
        r -= half
        c -= half
    n -= 1
# 2^1 * 2^1 일 때
if n == 1:
    if (r,c) == (0,0):
        pass
    if (r,c) == (0,1):
        result += 1
    if (r,c) == (1,0):
        result += 2
    if (r,c) == (1,1):
        result += 3
print(result)

아무리 생각해도 모르겠어서 블로그를 참고함..

그래프를 4등분으로 나눠 몇사분면인지를 알아내는 과정을 더 이상 쪼갤 수 없을때까지 반복.

  • 함수의 크기가 2ⁿ*2ⁿ인 2차원 배열을 4등분으로 나누려면 half = 2ⁿ//2
  • n -= 1 을 반복하며 나눌 수 있는 제일 작은 사분면의 위치까지 알아낸다.

 

반응형
Comments