Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

21-jung0115 #181

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open

21-jung0115 #181

wants to merge 23 commits into from

Conversation

jung0115
Copy link
Member

@jung0115 jung0115 commented Oct 23, 2024

➡️ 문제 풀이

🔗 문제 링크

프로그래머스 | DFS - 여행경로(Lv.2)

주어진 항공권을 모두 이용하여 여행경로를 짜려고 합니다. 항상 "ICN" 공항에서 출발합니다.

항공권 정보가 담긴 2차원 배열 tickets가 매개변수로 주어질 때, 방문하는 공항 경로를 배열에 담아 return 하도록 solution 함수를 작성해주세요.

제한사항

  • 모든 공항은 알파벳 대문자 3글자로 이루어집니다.
  • 주어진 공항 수는 3개 이상 10,000개 이하입니다.
  • tickets의 각 행 [a, b]는 a 공항에서 b 공항으로 가는 항공권이 있다는 의미입니다.
  • 주어진 항공권은 모두 사용해야 합니다.
  • 만일 가능한 경로가 2개 이상일 경우 알파벳 순서가 앞서는 경로를 return 합니다.
  • 모든 도시를 방문할 수 없는 경우는 주어지지 않습니다.

✔️ 소요된 시간

40분

✨ 수도 코드

단순히 주어진 항공권을 모두 사용하는 건 DFS로
도착 공항과 같은 공항에서 출발하는 항공권을 찾으면서 탐색해주면 구할 수 있지만,
알파벳 순서가 앞서는 경로를 찾아야 한다는 부분에서 고민이 생겼습니다

그래서 처음에는 애초에 항공권을 도착 공항의 알파벳순으로 정렬한 뒤에 탐색하면,
알파벳 순서가 앞서는 경로가 도출되지 않을까? 하고 단순하게 생각했습니다.

하지만 모든 항공권을 다 사용하는 경로 중에 알파벳 순서가 앞서는 경우를 찾는 것이고,
처음 생각한 방법대로 하면 모든 항공권을 다 사용하지 못할 가능성이 있었습니다
(여러 경로를 찾아보는 게 아니라 가장 앞선 항공권을 택해서 그 경로로만 탐색해버리는 방법이었습니다...)

그래서 여러 경로를 탐색해보면서, 모든 항공권을 다 사용했다면
이전에 탐색했던 경로보다 알파벳순으로 앞서는지 판단하는 작업이 필요했습니다

우선, 알파벳순으로 앞서는 것을 고려하지 않고 모든 항공권을 다 사용하는 경로를 찾는 방법은
아래 코드처럼 현재 공항과 출발 공항이 같은 항공권을 반복해서 탐색하면서 깊이 탐색을 해주면 됩니다.
그 과정에서 사용한 항공권의 개수를 세어주고, used 배열을 이용해 사용 유무를 표시해줬습니다

fun dfs(idx: Int, result: Array<String>) {
  if(idx == N + 1) {
    // 여기에 도달한 resullt는 모든 항공권을 다 사용하는 경로
    return
  }
  
  for(i: Int in 0..N-1) {
    if(!used[i] && cloneTickets[i][0] == result[idx - 1]) {
      used[i] = true
      result[idx] = cloneTickets[i][1]
      
      dfs(idx + 1, result)
      
      used[i] = false
    }
  }
}

이때 만들어진 Array 배열 중 알파벳 순으로 앞서는 게 어떤 것인지 판별해야 하는데,
Array끼리 비교를 하려면 반복문을 사용해야 했기 때문에
시간을 절약하기 위해 경로를 String에 따로 기록해주면서 대소비교로 어떤 게 앞서는지 판단하는 방법을 사용했습니다

예를 들어 현재 "ICN", "JFK", "HND"까지 탐색했다면,

{ "ICN", "JFK", "HND" }를 담고 있는 Array와
"ICNJFKHND"를 담고 있는 String을 동시에 기록하며
answer로 return할 값과 순서 비교를 할 값을 따로 관리해줬습니다!

이 방법을 이용해서
모든 항공권을 다 사용했을 때마다 순서 비교를 해줌으로써 답을 구해낼 수 있었습니다

✅ 최종 코드

class Solution {
  var N = 0

  lateinit var used: Array<Boolean>
  lateinit var cloneTickets: Array<Array<String>>
  
  lateinit var answer: Array<String>
  var answerStr: String = ""
  
  fun solution(tickets: Array<Array<String>>): Array<String> {
    val start = "ICN"
    cloneTickets = tickets.clone()
    
    N = tickets.size
    used = Array<Boolean>(N) { false }
    
    dfs(1, start, Array<String>(N + 1) { start })
    
    return answer
  }
  
  fun dfs(idx: Int, resultStr: String, result: Array<String>) {
    if(idx == N + 1) {
      if(answerStr.length == 0 || answerStr > resultStr) {
        answerStr = resultStr
        answer = result.clone()
      }
      return
    }
    
    for(i: Int in 0..N-1) {
      if(!used[i] && cloneTickets[i][0] == result[idx - 1]) {
        used[i] = true
        result[idx] = cloneTickets[i][1]
        
        dfs(idx + 1, resultStr + cloneTickets[i][1], result)
        
        used[i] = false
      }
    }
  }
}

📚 새롭게 알게된 내용

Copy link
Member

@janghw0126 janghw0126 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 BFS를 이용해서 풀어보았습니다!

  1. 티켓을 출발지와 목적지 기준으로 정렬해서, 알파벳 순서로 티켓을 순차탐색이 가능하도록 합니다.
  2. 그 다음, deque에 현재 경로와 남은 티켓들을 저장하여, BFS로 각 가능한 경로를 차례로 탐색합니다.
  3. 마지막으로, 남은 티켓이 없을 때의 경로가 알파벳 순서상 가장 빠른 여행 경로이므로 해당 경로를 반환합니다.
from collections import deque

def solution(tickets):
    answer = []
    # 출발지와 목적지 기준 정렬
    tickets.sort(key=lambda x: (x[0], x[1])) 
    dq = deque([(["ICN"], tickets)])
    
    while dq:
        path, remaining_tickets = dq.popleft()
        
        if not remaining_tickets:
            # 남은 티켓이 없으면 answer에 최종 경로 저장
            answer = path
            break
        
        for i, (start, dest) in enumerate(remaining_tickets):
            if start == path[-1]:
                dq.append((path + [dest], remaining_tickets[:i] + remaining_tickets[i+1:]))
    
    return answer

DFS를 이용해서 해결할 수도 있었군요! 한번 도전해보겠습니다
다음 PR도 팟팅입니당😘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants