[파알문] 3. 탐색 & 시뮬레이션

Nam_JU·2023년 7월 31일
0

Algorithm

목록 보기
14/20


23.07.31


회문 문자열 검사

  • 내코드
    13분, 시간복잡도 O(N)
    파이썬 정수 format 부분에서 헷갈려 검색함
"""
5
level
moon
abcba
soon
gooG
"""
n = int(input())
word = []
for i in range(n):
    s = input()
    word.append(s.upper())
cnt=0
for check in word:
    cnt+=1
    if check == check[::-1]:
        print("#%d YES" %cnt)
    else:
        print("#%d NO" %cnt)
  • 정답코드
    나의 경우 리스트를 활용해서 사용했으나 정답의 경우 리스트를 생성하지 않고 문자를 받고 바로 반을 나눠서 앞뒤 익덱싱으로 확인함
    메모리적으로 더 효율적인듯
n = int(input())
for i in range(n):
    s = input()
    s = s.upper()
    size = len(s)
    for j in range(size//2):
        if s[j]!=s[-1-j]:
            print("#%d NO" %(i+1))
            break
    else:
        print("#%d YES" %(i+1))

📍 파이썬 정수 format

>> i1=132
>> print("i have %d" %i1)
i have 132

숫자만 추출

  • 내코드
    13분
"""
g0en2Ts8eSoft
"""
s = input()
stringNum = ""
answer = 0
# 숫자- 문자열
for i in range(len(s)):
    if not s[i].isalpha():
        stringNum +=s[i]
# 앞의 0 제거
num = int(stringNum)

# 약수 개수
for i in range(1, num+1):
    if num%i==0:
        answer +=1
        
print(num)
print(answer)
  • 정답코드
s = input()
res = 0
for x in s:
    if x.isdecimal():
        res=res*10+int(x) # 앞자리 0을 제거 하고 숫자화 시키는 방법
print(res)
cnt =0
for i in range(1, res+1):
    if res%i==0:
        cnt+=1
print(cnt)       


나의 경우 문자열로 다 더한후 숫자변환을 처리해 앞자리 0을 제거하였으나 정답에서는 누적합*10+숫자를 사용하여 앞자리의 0을 제거했다.

📍 파이썬 숫자판별 함수

  • isdigit() : 해당 문자열이 숫자로 이루어져있는지 검사
  • isdecimal() : 해당 문자열이 0~9까지의 수로 이루어진 것인지 검사한다

카드 역배치

  • 내코드
    38분
"""
5 10
9 13
1 2
3 4
5 6
1 2
3 4
5 6
1 20
1 20
"""
change = []
card = [i for i in range(1,21)] #1-20숫자 카드 생성
for i in range(10): #입력값 튜플형태로 저장 
    a, b = map(int, input().split())
    change.append([a,b])

for a, b in change:
    n = (b-a+1)//2 # 반씩 회전
    while n>0:
        card[a-1], card[b-1] = card[b-1], card[a-1]
        n -= 1
        a+=1
        b-=1
    print(card)

print(card)
  • 정답코드
a = list(range(21)) # 더 간단하게 생성
for _ in range(10):
    s, e = map(int, input().split())
    for i in range((e-s+1)//2):
        a[s+i],a[e-i]=a[e-i],a[s+i]

a.pop(0)
for x in a:
    print(x, end=' ')

아이디어는 똑같았지만 구현부분에서 내코드는 비효율적인 부분이 많았다.

📍 파이썬 int형 리스트 문자열 출력

생각없이 join을 사용하여 리스트를 출력했다가 당황함.
현재 리스트가 숫자형이기 때문에 print(' '.join(map(str, card)))을 사용하여 출력해야한다.



23.08.01


두 리스트 합치기

  • 내코드
    8분, 시간복잡도: O(NlogN)
"""
3
1 3 5
5
2 3 6 7 9
"""

n = int(input())
a = list(map(int, input().split()))
m = int(input())
b = list(map(int, input().split()))

answer = a+b
print(' '.join(map(str, sorted(answer))))

값을 비교하는 형식으로 하려고 했는데 우선 쉬운 방법으로 풀었음... 위의 리스트 변환을 적용해보았다. 정답을 보니 안그래도 시간복잡도가 nlogn이기 때문에 이부분을 지적함

  • 정답코드
n = int(input())
a = list(map(int, input().split()))
m = int(input())
b = list(map(int, input().split()))
p1 =p2 = 0
answer = []


while p1<n and p2<m:  # 같지 않으면~ 식의 조건을 했었는데 크기비교로 사용하기
    if a[p1]>=b[p2]: # 값이 같을경우 복수로 넣어야 함으로 한쪽만 처리 (굳이 if3개를 만들 필요 없음)
        answer.append(b[p2])
        p2+=1
    elif a[p1]<b[p2]:
        answer.append(a[p1])
        p1+=1

# 예외처리 - append를 사용했다가 형식이 달라짐
if p1<n:
    answer=answer+a[p1:]
if p2<n:
    answer=answer+b[p2:]
print(' '.join(map(str,answer)))

시간복잡도를 줄이면서 효율적인 코드다
아이디어를 얻고 혼자 풀었을때 겪었던 에러사항들

  • while 조건문을 p1!=n or p2!=m으로 지정을 했더니 인덱싱 오류가 나왔다. 대소비교 사용하는 습관을 들이자.
  • if조건을 a가 작을때, b가 작을때, 같을때 3가지로 나눴더니 중복값이 사라졌음. 습관적으로 짜지말고 생각하면서 구현하자
  • 예외처리부분에서 append를 사용해 이차원 리스트가 생성되버림. 값 자체를 포함해서 저장이기 때문에 answer=answer+a[] 잊지말기

📍 파이썬 슬라이싱

a[start : end : step]
  • 각각 start, end, step 모두 양수와 음수를 가질 수 있다
  • start : 슬라이싱을 시작할 시작위치
  • end : 슬라이싱을 끝낼 위치로 end는 포함하지 않는다
  • step : stride(보폭)라고도 하며 몇개씩 끊어서 가져올지와 방향을 정함. 옵션으로 사용가능

수의 합

  • 내코드-오류
    1시간
n, m = map(int, input().split())
a = list(map(int, input().split()))
p1=0
p2=1
answer=0
while p1<n:
    save = a[p1] + a[p2]
    if save<m:
        p2+=1
    elif save==m:
        answer+=1
        p1=p2
        p2+=1
        save=0
    else: #save>m:  저장값이 클경우에 누적합 처리를 제대로 못함
        p1=p2
        p2+=1
        save=a[p1]
print(answer)

기본적인 아이디어는 같은데 디테일한 부분에서 크게 차이가 났다. 나의 경우 단순히 p1,p2를 이동시켜 합을 비교하는 부분만 생각했는데 정답코드의 경우 누적합이 클때, 작을때 같을때마다 누적합의 뺄셈부분을 염두해서 코드를 짰음.

  • 정답
n, m = map(int, input().split())
a = list(map(int, input().split()))

p1=0
p2=1
tot=a[0] #p1~p2전까지의 합
cnt=0
while True:
    if tot<m:
        if p2<n: #끝에 닿기 전까지 합하기
            tot+=a[p2]
            p2+=1
        else:
            break
    elif tot==m:
        cnt+=1 # 값 증가
        tot-=a[p1] #누적합 진행을 위해 처음값(p1) 빼기
        p1+=1 #한칸더 증가
    else:
        tot-=a[p1] #tot값이 크면 p1값 빼기
print(cnt)

격자판 최대합

  • 정답코드
    무의식적으로 격자판보면 상하좌우값부터 만들고 탐색하는 코드를 짜는데 이것도 고쳐야함...
    행&열 비교 대각선 비교 부분 외우기
    리스트 컨프리헨션 graph = [list(map(int, input().split())) for _ in range(n)] 사용하기
"""
5
10 13 10 12 15
12 39 30 23 11
11 25 50 53 15
19 27 29 37 27
19 13 30 13 19

"""

dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
n = int(input())
""" 이차원 배열 생성 코드 : 비효율적임
graph=[]
for _ in range(n):
    a = list(map(int, input().split()))
    graph.append(a)
"""
graph = [list(map(int, input().split())) for _ in range(n)]

max_num = -2147000000
# 행,열 합 비교
for i in range(n):
    sum1=sum2=0 #초기화
    for j in range(n):
        sum1+=graph[i][j]
        sum2+=graph[j][i]
    if sum1>max_num:
        max_num=sum1
    if sum2>max_num:
        max_num=sum2
sum1=sum2=0

# 대각선 합 비교
for i in range(n):
    sum1+=graph[i][i]
    sum2+=graph[i][n-i-1] #중요!
if sum1>max_num:
    max_num=sum1
if sum2>max_num:
    max_num=sum2
print(max_num)

사과나무

  • 정답코드
    n의길이를 반절씩 접어서 풀어보려고 했는데 피라미드 형식의 합을 어떤식으로 구해야할지 감이 안잡혔음
n = int(input())
graph = [list(map(int, input().split())) for _ in range(n)]

answer = 0
lt=rt=n//2
for i in range(n):
    for j in range(lt, rt+1): #변수를 사용해 열 범위 지정하기 
        answer += graph[i][j]
    if i<n//2: #x축을 중앙선으로 두어 열 범위 넓히기 
        lt-=1  
        rt+=1
    else:      #x축을 중앙선으로 두어 열 범위 좁히기   
        lt+=1
        rt-=1
print(answer)
  • 변수를 사용해서 범위값을 좁혀야함
  • 그래프에서 x축 중앙을 기준으로 열범위를 좁히고 늘리면서 피라미드모양의 위치합을 구할수 있다.


23.08.02


곳감

"""
5
10 13 10 12 15
12 39 30 23 11
11 25 50 53 15
19 27 29 37 27
19 13 30 13 19
3
2 0 3
5 1 2
3 1 4
"""

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

# 리스트로 받지말고 바로 변수로 받으면서 코드짬
# change = [list(map(int, input().split())) for _ in range(m)]

# 회전 배치
for i in range(m):
    h, t, k = map(int, input().split())
    if t==0:
        for _ in range(k):
            graph[h-1].append(graph[h-1].pop(0)) #왼쪽회전
    else:
        for _ in range(k):
            graph[h-1].insert(0,graph[h-1].pop()) #오른쪽 회전


# 모래시계 위치 합
lt=0
rt=n-1
answer=0
for i in range(n):
    for j in range(lt, rt+1):
        answer+=graph[i][j]
    if i<n//2:
        lt+=1
        rt-=1
    else:
        lt-=1
        rt+=1

print(answer)

예전 문제풀때 insert부분을 생각하지 못하고 append만 사용해서 에러가 난적이 있는데 또 틀림


봉우리

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

# 가장자리 0 초기화
graph.insert(0, [0]*n) #위 행추가
graph.append([0]*n)  #아래 행추가
for x in graph:
    x.insert(0,0) #왼쪽 가장자리 추가
    x.append(0) #오른쪽 가장자리 추가

cnt=0
for i in range(1, n+1):
    for j in range(1, n+1):
        """
        상하좌우 봉우리중 현재값 graph[i][j]보다 큰지 비교
        상하좌우 = graph[nx][ny]
        상하좌우 = graph[i+dx[k][j+dy[k]] 4바퀴 회전(for k in range(4))
        """
        if all(graph[i][j]> graph[i+dx[k]][j+dy[k]] for k in range(4)):
            cnt+=1
print(cnt)

📍 파이썬 any(), all()

파이썬 내장함수 중 any()all()이 둘은 아큐먼트로 iterable한 객체를 받는데 이 객체를 돌면서 조건을 검사해 답을 True/False의 답을 반환한다.

  • any() : 하나라도 True인게 있으면 True
  • all() : 모두 True여야 True 반환

스토쿠 검사

"""
1 4 3 6 2 8 5 7 9
5 7 2 1 3 9 4 6 8
9 8 6 7 5 4 2 3 1
3 9 1 5 4 2 7 8 6
4 6 8 9 1 7 3 5 2
7 2 5 8 6 3 9 1 4
2 3 7 4 8 1 6 9 5
6 1 9 2 7 5 8 4 3
8 5 4 3 9 6 1 2 7
"""



#3X3 탐색
#전체 행 탐색
#전체 열 탐색

def check(a):
    # 그래프 전체 행열 탐색
    for i in range(9):
        ch1=[0]*10 #행 체크
        ch2=[0]*10 #열 체크
        for j in range(9):
            ch1[a[i][j]]=1
            ch2[a[i][j]]=1
        if sum(ch1)!=9 or sum(ch2)!=9:
            return False

    # 3X3 그룹 탐색
    for i in range(3):
        for j in range(3):
            ch3=[0]*10
            # 선택된 그룹의 갯수 탐색
            for k in range(3):
                for s in range(3):
                    # i*3[행] j*3[열] 점점 늘어남
                    # 늘어난 값을 기준으로 k[행], s[열]이 갯수 탐색
                    ch3[a[i*3+k][j*3+s]]=1
                if sum(ch3)!=9:
                    return False
    return True

graph = [list(map(int, input().split())) for _ in range(9)]
if check(graph):
    print("YES")
else:
    print("NO")

격자판 회문수

"""
2 4 1 5 3 2 6
3 5 1 8 7 1 7
8 3 2 7 1 3 8
6 1 2 3 2 1 1
1 3 1 3 5 3 2
1 1 2 5 6 5 2
1 2 2 2 2 1 5
"""

#5개씩 끊어서 전체 탐색?
#가운데를 제외하고 n-1, n-2 값이 같은지 확인

graph = [list(map(int, input().split())) for _ in range(7)]
cnt=0
"""
전체 길이 7에서 5개씩만 끊어서 확인을 하면 되기 때문에 
i 회전의 기준점이 행을 기준으로 [0,1,2]까지만 돌면 된다 
i=0 j=[0,1,2,3,4]
i=1 j=[1,2,3,4,5]
i=2 j=[2,3,4,5,6]
"""
for i in range(3):
    for j in range(7):
        # 행 탐색 - 리스트의 슬라이싱 활용
        tmp=graph[j][i:i+5]
        if tmp==tmp[::-1]:
            cnt+=1
        # 열 탐색 - 열을 탐색할때는 리스트가 아님으로 for문으로 탐색을 할 수 밖에 없다
        """
        i=0, j=0, k=0 -> [0,0]!=[5,0] 열의 0, 4 비교
        i=0, j=0, k=1 -> [1,0]!=[3,0] 열의 1, 3 비교
        """
        for k in range(2):
            if graph[i+k][j]!=graph[i+5-k-1][j]:
                break
        else:
            cnt+=1
print(cnt)


참고자료
https://otugi.tistory.com/206

profile
개발기록

0개의 댓글