문제풀이/BackTracking

[PS] BOJ145000 테트로미노 ( BackTracking ) With 파이썬

IT록흐 2023. 6. 27. 17:19
반응형

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

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변

www.acmicpc.net

 

◎ 문제풀이

 

굉장히 어려운 문제였다. 

인터넷에서 풀이법을 찾아보았는데 다른 사람들도 노가다 한 사람을 제외하면 대부분 풀이를 참고해서 푼거 같다.

 

1. DFS 구현 및 발상

2. 최대값 가지치기 ( 백트래킹 )

 

두 가지 요소를 구현하기 어려웠다.

 

1. DFS 구현 및 발상

 

완전탐색을 할 때 DFS 알고리즘(재귀호출)을 사용해야 한다. 그 이유는 아래 포스팅에 잘 설명되어 있다. 

 

 

백준 14500 테트로미노

# 문제 ### DFS를 사용해서 합의 최댓값을 구하는 문제. (ㅜ 모양은 예외처리 합니다) 1. n 종이의 크기 (4 ≤ N, M ≤ 500) 2. 종이 한칸의 수는 (1<= aij <= 1,000) 3. 5개의 모양을 종이에 놓아서 합의 최대값

velog.io

 

위 링크의 풀이는 DFS가 'ㅗ' 모형을 탐색하지 못한다. 그래서  'ㅗ' 모형의 경우를 탐색하는 함수를 추가로 만들어야 한다. 추가로 만들지 않고 DFS 함수 하나로 모든 경우를 탐색하는 방법이 있다. 이는 아래 포스팅에 잘 설명되어 있다.

 

 

 

[파이썬]백준 14500 테트로미노

[파이썬]백준 14500 테트로미노

velog.io

 

 

2. 최대값 가지치기 ( 백트래킹 )

 

DFS만 구현해도 문제를 풀 수 있지만 시간이 엄청 걸린다. 이때 백트래킹을 적용하면 처리시간을 줄일 수 있다. 최소값 가지치기가 아닌 최댓값 가지치기를 해야한다.

 

max_value = max(map(max,board)) # 입력받은 이차원 배열에서 가장 큰 값 추출
#DFS
def dfs(x,y,current,length) :
  global ans
  if ans >= current + max_value*(4-length) : return
  #중략...

 

ans는 최댓값이 들어가야 한다. 현재까지 합(current)에서 가장 최댓값을 남은 블록만큼 더해도 ans보다 작거나 같다면 가지치기를 한다. 

 

 

◎ 코드

 

최적화된 코드  

#BOJ14500 테트로미노
import sys
input = sys.stdin.readline 

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

max_value = max(map(max,board)) #최대값

dx = [0,0,1,-1]
dy = [1,-1,0,0]
ans = 0 #결과

#유효성검사
def isvalidate(x,y) :
  if x >= 0 and y >= 0 and x < m and y < n : return True
  else : return False

#DFS
def dfs(x,y,current,length) :
  global ans
  if ans > current + max_value*(4-length) : return # 가지치기(백트래킹)
  if length == 4 : #블록이 4개인 경우
    ans = max(ans,current)
    return 
    
  for i in range(4) :
    nx = x + dx[i] 
    ny = y + dy[i] 
    if isvalidate(nx,ny) and not visited[ny][nx] :
        if length == 2 : # 'ㅗ' 모형이 가능하도록 좌표 유지
          visited[ny][nx] = True
          dfs(x,y,current+board[ny][nx],length+1)
          visited[ny][nx] = False
        visited[ny][nx] = True
        dfs(nx,ny,current+board[ny][nx],length+1)
        visited[ny][nx] = False

#문제풀이
for i in range(n) :
  for j in range(m) :
    visited[i][j] = True
    dfs(j,i,board[i][j],1)
    visited[i][j] = False
    
print(ans)

 

'ㅗ' 따로 계산한 코드

#BOJ14500 테트로미노
import sys
input = sys.stdin.readline 

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

max_value = max(map(max,board))

dx = [0,0,1,-1]
dy = [1,-1,0,0]

#'ㅗ' 이동 좌표
t = [
  [(1,0),(-1,0),(0,-1)],
  [(1,0),(-1,0),(0,1)],
  [(0,1),(0,-1),(1,0)],
  [(0,1),(0,-1),(-1,0)]
]

ans = 0

#유효성검사
def isvalidate(x,y) :
  if x >= 0 and y >= 0 and x < m and y < n : return True
  else : return False

#DFS
def dfs(x,y,total,lengh) :
  global ans

  if ans > total + max_value*(4-lengh) : return
  if lengh == 4 : #길이가 4인 경우
    ans = max(ans,total)
    return 
    
  for i in range(4) :
    nx = x + dx[i] 
    ny = y + dy[i] 
    if isvalidate(nx,ny) and not visited[ny][nx] :
        visited[ny][nx] = True
        dfs(nx,ny,total+board[ny][nx],lengh+1)
        visited[ny][nx] = False

# 'ㅗ' 모양
def tshape(x,y) :
  total = 0
  for i in range(4) :
    tmp = board[y][x]
    flag = True
    for j in range(3) :
      position = t[i][j] 
      tx = position[0]
      ty = position[1]
      nx = x + tx
      ny = y + ty
      if isvalidate(nx,ny) :
        tmp += board[ny][nx]
      else : flag = False

    total = ( max(total,tmp) if flag else total )
  return total

#문제풀이
for i in range(n) :
  for j in range(m) :
    visited[i][j] = True
    dfs(j,i,board[i][j],1)
    visited[i][j] = False
    ans = max(ans,tshape(j,i))
    
print(ans)

 

 

 

 

 

반응형