반응형

안녕하세요. 개발 중인 정주입니다.

 

오늘은 "프로그래머스(Lv.2) - 양궁 대회" 문제를 풀었습니다.

 


Github

 

GitHub - jeongju9216/SwiftAlgorithm: 스위프트 알고리즘

스위프트 알고리즘. Contribute to jeongju9216/SwiftAlgorithm development by creating an account on GitHub.

github.com

 

문제 링크

 

코딩테스트 연습 - 양궁대회

문제 설명 카카오배 양궁대회가 열렸습니다. 라이언은 저번 카카오배 양궁대회 우승자이고 이번 대회에도 결승전까지 올라왔습니다. 결승전 상대는 어피치입니다. 카카오배 양궁대회 운영위원

programmers.co.kr

 


풀이

이번 문제는 dfs + 완전 탐색 문제입니다.

 

1. 점수판을 초기화하고 dfs를 시작합니다.

라이언의 점수판을 0으로 초기화하여 dfs를 시작합니다.

 


      
var lionHistory: [Int] = Array(repeating: 0, count: 11)
dfs(lionHistory, info.reversed(), depth: 10, arrow: n) //info도 0~10으로 전달

이번 문제는 낮은 점수를 많이 맞춘 사람을 기준으로 정렬해야 합니다.

따라서 점수를 저장할 때 0~10 순서로 저장할 것입니다.

index의 통일을 위해 info도 역순으로 전달했습니다.

 

2. DFS + 완전 탐색 로직

DFS + 완전 탐색 로직의 핵심은 어피치를 이기느냐 지느냐입니다.

  • 해당 점수를 얻는 경우(어피치보다 많은 화살을 쏘는 경우)
  • 해당 점수를 얻지 않는 경우(어피치보다 많은 화살을 못 쏘는 경우, 그냥 안 쏘는 경우)

위 두 가지를 탐색하여 2^10 가지 경우를 탐색하는 것입니다.

 

2-1. DFS의 종료 조건


      
if depth == 0 || arrow <= 0 { //10번째 시도 || 화살을 다 쏨
copyHistory[0] = depth == 0 ? arrow : copyHistory[0]
let score = getScoreGap(history, info) //점수차 확인
if maxScore == score { //최대 점수 차이면 점수판 저장
maxHistory.append(copyHistory)
} else if maxScore < score { //최대 점수 갱신
maxScore = score
maxHistory = [copyHistory]
}
return
}

DFS의 종료 조건먼저 살펴보겠습니다.

  1. 10개의 점수를 모두 살펴본 경우
  2. 화살이 다 떨어진 경우

위 두 가지 경우인 경우 어피치와 라이언의 점수차를 구합니다.

  • 점수차 == 현재 최대 점수 : 점수판을 추가합니다.
  • 점수차 > 현재 최대 점수 : 최대 점수와 점수판을 갱신합니다.
  • 점수차 < 현재 최대 점수 : 아무 동작을 하지 않습니다.

점수판을 저장하는 이유는 4번에서 설명합니다.

 

2-2. 해당 점수를 얻는 경우

여기서 해당 점수를 나타내는 인자는 depth 입니다.

dfs 시작을 10으로 했으므로 10점부터 9점, 8점 ... 0점 순서로 확인합니다.


      
let shooting = info[depth] + 1
if arrow >= shooting { //어피치를 이길 수 있는 경우
copyArrow -= shooting
copyHistory[depth] = shooting //쏜 경우
dfs(copyHistory, info, depth: depth - 1, arrow: copyArrow) //다음 점수로 이동
copyHistory[depth] = 0
}

점수를 얻을 수 있는 경우는 남은 화살 수 >= 어피치가 쏜 화살 수 + 1 일 때 입니다.

이때는 점수판에 표시하고 남은 화살 수에서 (어피치가 쏜 화살 수 + 1)을 빼서 다음 점수로 dfs 탐색을 합니다.

탐색을 종료하고 되돌아오면 점수판 표시를 지웁니다.

 

2-3. 해당 점수를 얻지 않는 경우

화살이 부족해 어피치를 이기지 못하거나 화살이 충분해도 그냥 안 쏘는 경우를 탐색합니다.


      
dfs(copyHistory, info, depth: depth - 1, arrow: arrow)

arrow와 점수판은 그대로 설정하고 depth만 줄여 dfs 탐색 합니다.

 

3. 어피치를 못 이기는 경우를 처리합니다.

최대 점수가 전혀 갱신이 안 되었다면 어피치를 이기지 못하는 경우입니다.

이때는 [-1]을 return 합니다.

 

4. 어피치를 이기는 경우를 처리합니다.

어피치를 이길 수 있는 경우, 최대 점수 차로 이겨야 하며 그중에서도 낮은 점수를 많이 맞춘 경우를 return 해야 합니다.

그래서 2-1에서 점수판을 저장해둔 것입니다.

 

점수판을 낮은 점수를 많이 맞춘 것으로 오름차순 정렬합니다.


      
maxHistory.sort {
$0.filter { $0 != 0 }.count > $1.filter { $0 != 0 }.count
}

같은 화살 개수이고 맞춘 점수가 같을 때 낮은 점수를 많이 맞췄다는 것은 맞춘 점수의 스펙트럼이 넓다는 것을 의미합니다.

 

예를 들어, 화살이 5개인 경우 1점에 5개를 맞춘 것과 5점에 1개 0점에 4개를 맞출 수 있습니다.

0점을 더 많이 맞춘 후자의 경우 5, 0으로 맞춘 점수의 종류가 더 많습니다.

 

이 원리로 2차원 배열을 sort 합니다.

 

5. 역순 배열을 return 합니다.

점수 비교의 편의를 위해 0 ~ 10점 순서로 작업을 했었습니다.

문제에서 요구하는 것은 10 ~ 0점 순서이므로 역순 배열을 반환합니다.

 

후기

문제의 원리 자체는 쉬우나 코드를 짜기 어려웠던 문제였습니다.

왜 2레벨인지 모르겠는... 그런 문제였습니다 ㅠㅠ

 

감사합니다!

 

 


전체 코드


      
import Foundation
var maxHistory: [[Int]] = [] //최대 점수 차이인 점수판
var maxScore: Int = -1 //최대 점수 차이
func dfs(_ history: [Int], _ info: [Int], depth: Int, arrow: Int) {
var copyHistory = history
var copyArrow = arrow
if depth == 0 || arrow <= 0 { //10번째 시도 || 화살을 다 쏨
copyHistory[0] = depth == 0 ? arrow : copyHistory[0]
let score = getScoreGap(history, info) //점수차 확인
if maxScore == score { //최대 점수 차이면 점수판 저장
maxHistory.append(copyHistory)
} else if maxScore < score { //최대 점수 갱신
maxScore = score
maxHistory = [copyHistory]
}
return
}
let shooting = info[depth] + 1
if arrow >= shooting { //어피치를 이길 수 있는 경우
copyArrow -= shooting
copyHistory[depth] = shooting //쏜 경우
dfs(copyHistory, info, depth: depth - 1, arrow: copyArrow) //다음 점수로 이동
copyHistory[depth] = 0
}
//안 쏜 경우
dfs(copyHistory, info, depth: depth - 1, arrow: arrow)
}
func getScoreGap(_ history: [Int], _ info: [Int]) -> Int {
var lion = 0, apeach = 0
for (i, score) in history.enumerated() {
if score == 0 {
apeach += (info[i] > 0 ? i : 0)
} else {
lion += i
}
}
return lion - apeach
}
func solution(_ n:Int, _ info:[Int]) -> [Int] {
var result: [Int] = []
var lionHistory: [Int] = Array(repeating: 0, count: 11)
dfs(lionHistory, info.reversed(), depth: 10, arrow: n) //info도 0~10으로 전달
// print("maxScore: \(maxScore) / maxHistory: \(maxHistory)")
guard maxScore > 0 else { //못 이기는 경우
return [-1]
}
//같은 점수일 때 적은 점수에 많이 쏜거면 쏜 점수의 종류가 많다.
maxHistory.sort {
$0.filter { $0 != 0 }.count > $1.filter { $0 != 0 }.count
}
// print("maxHisotry: \(maxHistory)")
return maxHistory.first!.reversed()
}

아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

 

 

반응형
유정주