목차
색종이 만들기(#2630)
- Problem
아래 <그림 1>과 같이 여러개의 정사각형칸들로 이루어진 정사각형 모양의 종이가 주어져 있고, 각 정사각형들은 하얀색으로 칠해져 있거나 파란색으로 칠해져 있다. 주어진 종이를 일정한 규칙에 따라 잘라서 다양한 크기를 가진 정사각형 모양의 하얀색 또는 파란색 색종이를 만들려고 한다. 전체 종이의 크기가 N×N(N=2k, k는 1 이상 7 이하의 자연수) 이라면 종이를 자르는 규칙은 다음과 같다. 전체 종이가 모두 같은 색으로 칠해져 있지 않으면 가로와 세로로 중간 부분을 잘라서 <그림 2>의 I, II, III, IV와 같이 똑같은 크기의 네 개의 N/2 × N/2색종이로 나눈다. 나누어진 종이 I, II, III, IV 각각에 대해서도 앞에서와 마찬가지로 모두 같은 색으로 칠해져 있지 않으면 같은 방법으로 똑같은 크기의 네 개의 색종이로 나눈다. 이와 같은 과정을 잘라진 종이가 모두 하얀색 또는 모두 파란색으로 칠해져 있거나, 하나의 정사각형 칸이 되어 더 이상 자를 수 없을 때까지 반복한다. 위와 같은 규칙에 따라 잘랐을 때 <그림 3>은 <그림 1>의 종이를 처음 나눈 후의 상태를, <그림 4>는 두 번째 나눈 후의 상태를, <그림 5>는 최종적으로 만들어진 다양한 크기의 9장의 하얀색 색종이와 7장의 파란색 색종이를 보여주고 있다. 입력으로 주어진 종이의 한 변의 길이 N과 각 정사각형칸의 색(하얀색 또는 파란색)이 주어질 때 잘라진 하얀색 색종이와 파란색 색종이의 개수를 구하는 프로그램을 작성하시오.
- Hint
분할 정복에 대한 개념을 알아두자
- Solution
분할 정복을 이용한 풀이
import sys
k = int(input())
draw = []
w = 0
b = 0
for _ in range(k):
draw.append(list(map(int, sys.stdin.readline().split())))
print(draw)
def count(x, y, n):
global w, b
color = draw[x][y]
for i in range(x, x + n):
for j in range(y, y + n):
if color != draw[i][j]:
count(x, y, n // 2)
count(x, y + n // 2, n // 2)
count(x + n // 2, y, n // 2)
count(x + n // 2, y + n // 2, n // 2)
return
if color == 0:
w += 1
else:
b += 1
count(0, 0, k)
print(w, b, sep="\n")
재귀함수가 가지는 변수로 k값과 저장된 리스트를 불러와서 사용을 하려고 하였는데 리스트의 크기가 커지는 경우 계속하여 전체 리스트를 불러와야하기 때문에 효율적이지 못했다.
따라서 정해로는 리스트를 받아놓고 분할되는 부분 만큼을 기존 리스트에서 선택하여 뽑아내는 방식으로 문제를 풀어야한다.
따라서 재귀함수에 시작 부분을 기재하여준다.
쿼드트리(#1992)
- Problem
흑백 영상을 압축하여 표현하는 데이터 구조로 쿼드 트리(Quad Tree)라는 방법이 있다. 흰 점을 나타내는 0과 검은 점을 나타내는 1로만 이루어진 영상(2차원 배열)에서 같은 숫자의 점들이 한 곳에 많이 몰려있으면, 쿼드 트리에서는 이를 압축하여 간단히 표현할 수 있다. 주어진 영상이 모두 0으로만 되어 있으면 압축 결과는 "0"이 되고, 모두 1로만 되어 있으면 압축 결과는 "1"이 된다. 만약 0과 1이 섞여 있으면 전체를 한 번에 나타내지를 못하고, 왼쪽 위, 오른쪽 위, 왼쪽 아래, 오른쪽 아래, 이렇게 4개의 영상으로 나누어 압축하게 되며, 이 4개의 영역을 압축한 결과를 차례대로 괄호 안에 묶어서 표현한다 위 그림에서 왼쪽의 영상은 오른쪽의 배열과 같이 숫자로 주어지며, 이 영상을 쿼드 트리 구조를 이용하여 압축하면 "(0(0011)(0(0111)01)1)"로 표현된다. N ×N 크기의 영상이 주어질 때, 이 영상을 압축한 결과를 출력하는 프로그램을 작성하시오.
- Hint
위의 문제를 확인해서 같이 해보자
- Solution
분할 정복을 이용한 풀이
import sys
k = int(input())
draw = []
res = []
for _ in range(k):
a = sys.stdin.readline().rstrip()
draw.append(list(map(int, str(a))))
def count(x, y, n):
global w, b
color = draw[x][y]
for i in range(x, x + n):
for j in range(y, y + n):
if color != draw[i][j]:
res.append("(")
count(x, y, n // 2)
count(x, y + n // 2, n // 2)
count(x + n // 2, y, n // 2)
count(x + n // 2, y + n // 2, n // 2)
res.append(")")
return
if color == 0:
res.append("0")
else:
res.append("1")
count(0, 0, k)
print(*res, sep="")
색종이 만들기에서의 input 값을 가공하는 부분만 바꿔서 하면된다.
map(int, str(data))
종이의 개수(#1780)
- Problem
N×N크기의 행렬로 표현되는 종이가 있다. 종이의 각 칸에는 -1, 0, 1 중 하나가 저장되어 있다. 우리는 이 행렬을 다음과 같은 규칙에 따라 적절한 크기로 자르려고 한다. 만약 종이가 모두 같은 수로 되어 있다면 이 종이를 그대로 사용한다. (1)이 아닌 경우에는 종이를 같은 크기의 종이 9개로 자르고, 각각의 잘린 종이에 대해서 (1)의 과정을 반복한다. 이와 같이 종이를 잘랐을 때, -1로만 채워진 종이의 개수, 0으로만 채워진 종이의 개수, 1로만 채워진 종이의 개수를 구해내는 프로그램을 작성하시오.
- Hint
4개 -> 9개
- Solution
분할 정복을 이용한 풀이
import sys
k = int(input())
draw = []
res = []
m = 0
z = 0
p = 0
for _ in range(k):
draw.append(list(map(int, sys.stdin.readline().split())))
def count(x, y, n):
global m, z, p
color = draw[x][y]
for i in range(x, x + n):
for j in range(y, y + n):
if color != draw[i][j]:
t = n // 3
count(x, y, t)
count(x, y + t, t)
count(x, y + (2 * t), t)
count(x + t, y, t)
count(x + t, y + t, t)
count(x + t, y + (2 * t), t)
count(x + (2 * t), y, t)
count(x + (2 * t), y + t, t)
count(x + (2 * t), y + (2 * t), t)
return
if color == 0:
z += 1
elif color == 1:
p += 1
else:
m += 1
count(0, 0, k)
print(m, z, p, sep="\n")
색종이 만들기 문제에서 9개로 분류해주면 된다.
곱셈(#1629)
- Problem
자연수 A를 B번 곱한 수를 알고 싶다. 단 구하려는 수가 매우 커질 수 있으므로 이를 C로 나눈 나머지를 구하는 프로그램을 작성하시오.
- Hint
색종이 문제는 4개로 나누는 경우였는데 그건 기하학의 경우이고 자연수 곱 또한 n번 곱하는 것을 n/2 n/2 번 곱한것 끼리 곱해주는 분할 정복법을 사용하면 곱하는 연산 횟수를 줄일수 있다.
- Solution
분할 정복을 이용한 풀이
a, b, c = map(int, input().split())
res = 1
def expdiv(res, a, b, c):
if b == 1:
res = res * a % c
return res
else:
td = expdiv(res, a, b // 2, c)
if b % 2 == 0:
return td * td % c
else:
return td * td * a % c
print(expdiv(res, a, b, c))
매 결과값에 %로 나머지를 구하기에 메모리초과가 발생하지 않는다.
이항 계수 3(#11401)
- Problem
자연수 \(N\)과 정수 \(K\)가 주어졌을 때 이항 계수 \(\binom{N}{K}\)를 1,000,000,007로 나눈 나머지를 구하는 프로그램을 작성하시오.
- Solution
분할 정복을 이용한 풀이
n,k= map(int, input().split())
p= 1000000007
def factorial(N):
n=1
for i in range(2,N+1):
n=(n*i)%p
return n
def expdiv(n,e):
if e==0:
return 1
elif e==1:
return n
else:
td=expdiv(n,e//2)
if e%2:
return td*td*n%p
else:
return td*td%p
top = factorial(n)%p
bottom = expdiv(factorial(n-k)*factorial(k)%p,p-2)
print(top*bottom%p)
따로 블로그에 포스팅 하였다.
행렬 곱셈(#2740)
- Problem
N*M크기의 행렬 A와 M*K크기의 행렬 B가 주어졌을 때, 두 행렬을 곱하는 프로그램을 작성하시오.
- Solution
분할 정복을 이용한 풀이
import sys
input = sys.stdin.readline
a=[]
b=[]
n,m = map(int, input().split())
for _ in range(n):
a.append(list(map(int, input().split())))
m,k = map(int, input().split())
for _ in range(m):
b.append(list(map(int, input().split())))
res=[[0 for _ in range(k)]for _ in range(n)]
td=0
for i in range(n):
for j in range(k):
for l in range(m):
td+=a[i][l]*b[l][j]
print(td,end=' ')
td=0
print()
풀긴 풀었는데 이건 삼중 for 문이지 분할정복 방식이 아닌것 같다.
슈트라센 알고리즘(행렬곱을 할 시에 분배 법칙등으로 계산식이 줄은 계산식)을 사용하는 방식이 분할 정복 방식이라고 하던데.. 슈트라센 알고리즘도 결국엔 각 포지션마다 다른 계산식을 사용하는 것이지 분할정복 방식인지는 모르겠다.
아마도 문제 설명에 나와있기에 다음 문제인 분할정복을 이용한 행렬곱 문제를 풀이하기 위하여 행렬곱을 하는 방식을 알려주는 것 같다.
혹시나 슈트라센 알고리즘을 통해 분할과 정복 방식을 사용하는 코드를 짜셨다면 댓글 부탁드리겠습니다.
행렬 제곱(#10830)
- Problem
크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.
- Hint
행렬곱에 대한 이해가 필요하다.
- Solution
분할 정복을 이용한 풀이
import sys
input = sys.stdin.readline
def matrixexp(a,b):
res=[[0 for _ in range(n)]for _ in range(n)]
for i in range(n):
for j in range(n):
td=0
for l in range(n):
td+=a[i][l]*b[l][j]
res[i][j]=td%1000
return res
def conquer(res,k):
if k==1:
for i in range(n):
for j in range(n):
res[i][j]=res[i][j]%1000
return res
else:
tplist=[]
tplist= conquer(res,k//2)
if k%2:
return matrixexp(matrixexp(tplist,tplist),res)
else:
return matrixexp(tplist,tplist)
a=[]
n,k = map(int, input().split())
for _ in range(n):
a.append(list(map(int, input().split())))
lists=conquer(a,k)
for list in lists:
print(*list)
피보나치 수 6(#11444)
- Problem
피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다. 이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n ≥ 2)가 된다. n=17일때 까지 피보나치 수를 써보면 다음과 같다. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 n이 주어졌을 때, n번째 피보나치 수를 구하는 프로그램을 작성하시오.
- Hint
피보나치 식을 행렬로 나타내보
- Solution
분할 정복을 이용한 풀이
n=int(input())
a=[[1,1],[1,0]]
def fibo(n):
if n==0 or n==1:
return a
else:
tl=fibo(n//2)
if n%2:
return matricexp(matricexp(tl,tl),a)
else:
return matricexp(tl,tl)
def matricexp(a,b):
res=[[0,0],[0,0]]
for i in range(2):
for j in range(2):
td=0
for l in range(2):
td+= a[i][l]*b[l][j]%1000000007
res[i][j]=td%1000000007
return res
print(fibo(n-1)[0][0])
피보나치를 구할때 행렬의 제곱꼴로 나타내면 분할과 정복을 사용하여 쉽게 구할 수 있다.
히스토그램에서 가장 큰 직사각형(#6549)
- Problem
히스토그램은 직사각형 여러 개가 아래쪽으로 정렬되어 있는 도형이다. 각 직사각형은 같은 너비를 가지고 있지만, 높이는 서로 다를 수도 있다. 예를 들어, 왼쪽 그림은 높이가 2, 1, 4, 5, 1, 3, 3이고 너비가 1인 직사각형으로 이루어진 히스토그램이다. 히스토그램에서 가장 넓이가 큰 직사각형을 구하는 프로그램을 작성하시오.
- Hint
힌트입력 (==)
- Solution
분할 정복을 이용한 풀이(그리디)
# 분할 정복 풀이 (그리디)
import sys
input = sys.stdin.readline
def divandconq(l,r):
if l==r:
return tc[l]
# 가운데있는 부피 최대 구하기
m=l+(r-l)//2
lp=m
rp=m+1
mH=min(tc[lp],tc[rp])
mW=2
mAM=mH*mW
while (l tc[rp+1]:
lp-=1
cH=tc[lp]
else:
rp+=1
cH=tc[rp]
mH = min(mH,cH)
mW+=1
mAM = max(mAM,mH*mW)
return max(divandconq(l,m),divandconq(m+1,r),mAM)
while 1:
tc = list(map(int, input().split()))
if tc[0]==0:
break
print(divandconq(1,len(tc)-1))
스택을 이용한 풀이
# 스택 풀이
import sys
input = sys.stdin.readline
def stack(list):
rec=[]
recMA=0
for h in list:
cnt = 0
while len(rec) != 0 and rec[-1][1] > h:
# cnt는 현재 높이의 히스토그램이 앞으로 총 몇칸이 유효한지를 나타낸다.
cnt += rec[-1][0]
recMA = max(recMA, cnt * rec[-1][1])
rec.pop()
cnt += 1
rec.append([cnt,h])
cnt = 0
while len(rec) != 0:
cnt += rec[-1][0]
recMA = max(recMA, cnt*rec[-1][1])
rec.pop()
return recMA
while 1:
tc = list(map(int, input().split()))
if tc[0]==0:
break
print(stack(tc[1:]))
'Baekjoon > Stepbystep' 카테고리의 다른 글
[백준/python] 그리디 알고리즘 전체 풀이(19단계) (0) | 2023.01.21 |
---|---|
[백준/python] 누적 합 전체 풀이(18단계) (0) | 2023.01.17 |
[백준/python] 동적 계획법1 전체 풀이(16단계) (0) | 2023.01.14 |
[백준/python] 백트래킹 전체 풀이(15단계) (0) | 2022.09.26 |
[백준/python] 정수론 및 조합론 전체 풀이(14단계) (0) | 2022.09.02 |