코딩테스트

프로그래머스 - 위클리 챌린지 3주차 - 퍼즐 조각 채우기 - swift

vapor3965 2021. 8. 17. 18:40

 

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

 

코딩테스트 연습 - 3주차

[[1,1,0,0,1,0],[0,0,1,0,1,0],[0,1,1,0,0,1],[1,1,0,1,1,1],[1,0,0,0,1,0],[0,1,1,1,0,0]] [[1,0,0,1,1,0],[1,0,1,0,1,0],[0,1,1,0,1,1],[0,0,1,0,0,0],[1,1,0,1,1,0],[0,1,0,0,0,0]] 14 [[0,0,0],[1,1,0],[1,1,1]] [[1,1,1],[1,0,0],[0,0,0]] 0

programmers.co.kr

 

 

구현문제에 완전탐색과도 같다. 

여기서 핵심을 빠르게 짚어야하는데, 조각을 보드판에 채우기위해서는 꼭 맞아떨어져야 한다는 점이다.

즉, 하나의 조각이 보드판의 빈칸들중에서 여러곳으로 들어갈 수 있다고 해도, 한곳에만 들어가면 된다는 점이다.

 

우선, 문제를 풀기위해서는 도형을 회전할 줄 알아야한다. 

 

도형을 찾기위해서는, 우선 테이블위에 연속된 1들을 bfs로 탐색하여 연속된 도형들을 찾아낸다.

그리고, 연속된도형들을 [(Int, Int)]로 만들어주는데, 항상 첫번째 값이 0,0이 되도록 만들어준다. ( 나중에 이 도형을 가지고 보드판에 넣을때 편하기 위함 ) 

 

그러면, 도형을 찾았으면, 회전하는데, 총 3번회전한 값을 각각 [(Int, Int)] 로 만들어준다. 

마찬가지로 회전한 값의 첫번째값이 0,0되도록 해준다.

 

그럼 각 도형마다 4개의 연속된 (Int,Int)들이 존재하게 되고,

보드판을 전부탐색하면서, 들어갈 수 있는 도형을 넣어주면 된다. 

 

처음에 한곳에만 들어가면 된다는 점을 제대로 인지하지 못하고, 순열로 만들어서 탐색하느라 시간초과됐다. 

 

func solution(_ game_board:[[Int]], _ table:[[Int]]) -> Int {
    let N = table.count
    let dir = [(0,1),(0,-1),(1,0),(-1,0)]
    
    func blockList() -> [[(Int,Int)]] {
        var check = Array(repeating: Array(repeating: false, count: N ), count: N )
        var blockList: [[(Int,Int)]] = []
        for i in 0..<N {
            for j in 0..<N {
                if !check[i][j], table[i][j] == 1 {
                    check[i][j] = true
                    var list: [(Int,Int)] = []
                    var queue = [(i,j)]
                    while !queue.isEmpty {
                        let f = queue.removeFirst()
                        list.append(f)
                        for k in 0..<4 {
                            let n = (f.0+dir[k].0, f.1+dir[k].1)
                            if n.0>=N||n.1>=N||n.0<0||n.1<0||check[n.0][n.1]||table[n.0][n.1] == 0 { continue }
                            check[n.0][n.1] = true
                            queue.append((n))
                        }
                    }
                    let f = list.first!
                    blockList.append(list.map{ ($0.0 - f.0, $0.1 - f.1)})
                }
            }
        }
        return blockList
    }
    
    func rotateBlockList() -> [[[(Int, Int)]]] {
        return blockList().map{ block -> [[(Int, Int)]] in
            var list: [[(Int,Int)]] = []
            list.append(block)
            var t = block.map{(-$0.1, $0.0)}
            list.append(t.map{ ($0.0 - t.first!.0, $0.1 - t.first!.1)})
            t = block.map{($0.1, -$0.0)}
            list.append(t.map{ ($0.0 - t.first!.0, $0.1 - t.first!.1)})
            t = block.map{(-$0.0, -$0.1)}
            list.append(t.map{ ($0.0 - t.first!.0, $0.1 - t.first!.1)})
            return list
        }
    }
    
    let blocks = rotateBlockList()
    var board = game_board
    var blockCheck = Array(repeating: false, count: blocks.count)
    
    func find() -> Int {
        var sum = 0
        for i in 0..<N {
            for j in 0..<N {
                for l in 0..<blocks.count {
                    if blockCheck[l] { continue }
                    for k in 0..<4 {
                        if board[i][j] == 0 {
                            let block = blocks[l][k]
                            var isFaile = false
                            
                            // 우선 넣어본다.
                            block.forEach {
                                let n = (i+$0.0, j + $0.1)
                                if n.0>=N||n.1>=N||n.0<0||n.1<0{
                                    isFaile = true
                                    return
                                } else if board[n.0][n.1] >= 1 {
                                    isFaile = true
                                    board[n.0][n.1] += 1
                                } else {
                                    board[n.0][n.1] += 1
                                }
                            }
     
                            // 주변에 여백이있는지 확인한다.
                            block.forEach {
                                let n = (i+$0.0, j + $0.1)
                                if n.0>=N||n.1>=N||n.0<0||n.1<0 { return }
                                
                                for m in 0..<4 {
                                    let nn = (n.0+dir[m].0, n.1+dir[m].1)
                                    if nn.0>=N||nn.1>=N||nn.0<0||nn.1<0 {
                                        continue
                                    }
                                    if board[nn.0][nn.1] == 0 {
                                        isFaile = true
                                        break
                                    }
                                }
                            }
                            
                            // 실패했다면, 다시 넣은 도형을 걷어준다.
                            if isFaile {
                                block.forEach {
                                    let n = (i+$0.0, j + $0.1)
                                    if n.0>=N||n.1>=N||n.0<0||n.1<0{ return }
                                    board[n.0][n.1] -= 1
                                }
                                continue
                            } else {
                                blockCheck[l] = true
                                sum+=block.count
                                break
                            }
                        }
                    }
                }
            }
        }
        return sum
    }

    return find()
}