문제 URL

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

 

 

문제 설명 요약

  • 키가 작은 학생이 더 큰 학생보다 앞에 서도록 줄 세우기

 

문제 풀이

  • 위상정렬 사용
  • 키 비교 값을 방향을 가진 간선 정보로 사용
  • indegree의 값을 통해 우선적으로 위치해야 하는 정점들을 파악해나감
    • 주어진 키 비교값을 통해 정점간의 선후 관계를 indegree 배열로 표현
      만약 1 2 라면 1 -> 2 이므로, 2의 indegree 값에 1이 추가됨
    • 맨 처음부터 indegree의 값이 0인 정점들을 모두 큐에 추가
      (indegree가 0이라는 것은 곧 반드시 해당 정점보다 앞에 위치해야 하는 정점이 없다는 것과 같은 의미)
    • 큐가 빌 때까지 돌면서, 큐에서 빼낸 정점에서 뻗어나가는 정점들의 indegree 값을 1 감소시킴
      (큐에서 빼낸 정점을 의미적으로 삭제하기 위함)
    • 감소시킨 indegree값이 0이라면 앞선 로직처럼 큐에 추가

 

Swift를 활용한 풀이

let nm = readLine()!.split(separator: " ").map{Int($0)!}
var ind = Array(repeating: 0, count: nm[0]+1) // 1-index
var adj:[Int:[Int]] = [:]

for n in 1...nm[0] { adj[n] = [] }

for _ in 0..<nm[1] {
    let ab = readLine()!.split(separator: " ").map{Int($0)!}
    ind[ab[1]] += 1
    adj[ab[0]]!.append(ab[1])
}

var q:[Int] = []
for i in 1...nm[0] {
    if(ind[i] == 0) { q.append(i) } // 처음부터 indegree가 0인 정점을 큐에 추가해둠
}

var answer = ""
while(!q.isEmpty) {
    // 큐에 추가된 정점을 하나씩 꺼내어, 해당 정점에서 뻗어나가는 정점들의 indegree를 1 감소시킴
    // 감소된 indegree값이 0이라면, 해당 정점의 앞에 위치해야 하는 정점들이 모두 사라진 것으로 간주되므로 큐에 추가
    let cur = q.removeFirst()
    answer += "\(cur) "
    for n in adj[cur]! {
        ind[n] -= 1
        if(ind[n] == 0) { q.append(n) }
    }
}

print(answer)

 

 

JAVA를 활용한 풀이

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());

        int[] ind = new int[N+1];
        HashMap<Integer, ArrayList<Integer>> adj = new HashMap<>();

        for(int i=0; i<M; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            ind[b]++;

            ArrayList<Integer> list = adj.getOrDefault(a, new ArrayList<Integer>());
            list.add(b);
            adj.put(a, list);
        }

        Queue<Integer> q = new LinkedList<>();
        for(int i=1; i<=N; i++) {
            if(ind[i] == 0) q.offer(i);
        }

        while(!q.isEmpty()) {
            int cur = q.poll();
            sb.append(cur).append(" ");

            if(adj.get(cur) != null) {
                for(int n : adj.get(cur)) {
                    ind[n]--;
                    if(ind[n] == 0) q.offer(n);
                }
            }
        }

        System.out.println(sb);
    }
}

 

코드 실행 시, "실행 시 검은 화면만 나오는 경우"  혹은 "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"의 오류가 발생하는 경우 (로드 과정에서 컴포넌트 속성을 수정하는 경우 ex) label.text = "...")

 

이번 경우에는 화면 이동을 위해 SceneDelegate.swift 내에서, 아래와 같이 코드를 추가해서 문제가 발생함

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }

    let window = UIWindow(windowScene: windowScene)
    let rootVC = HomeViewController()   // 오류 발생 원인 코드
    let navigationController = UINavigationController(rootViewController: rootVC)

    window.rootViewController = navigationController
    self.window = window
    window.makeKeyAndVisible()
}

 

StoryBoard 기반의 UIKit를 사용하기 때문에, 단순히 저렇게 생성자를 호출하는 방식으로는 VC가 정상적으로 생성되지 않음

아래와 같이 identifier를 기반으로 instantiateViewController 메소드를 호출하여 초기화해야함

// 해결 코드
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewController(identifier: "HomeViewController") as! HomeViewController

 

 

문제 URL

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

 

 

문제 설명 요약

  • 최소힙을 활용하여 카드 묶음의 값 정렬
  • 작은 값부터 꺼내고 더해가며 최소 비교 횟수 구하기

 

문제 풀이

  • 최소힙 직접 구현 필요 (스위프트는 힙 자료구조 없음)
    • Heap :: push -> 추가되는 값을 힙 내 원소들과 비교하여 저장 
    • Heap :: pop -> 최소힙의 값(최상단 루트 값) 삭제 및 반환

 

Swift를 활용한 풀이

 

var heap = [0] // 1-based-index
let N = Int(readLine()!)!
for _ in 0..<N { push(Int(readLine()!)!) }

// 카드 묶음의 값을 두 개씩 꺼내어 합쳐나가는 과정 
// 합치는 것의 대상 두 묶음을 답에 누적 & 합쳐진 값은 새로운 카드 묶음으로 취급(push)
var answer = 0
while(heap.count > 2) {
    let a = pop()
    let b = pop()
    answer += a+b
    push(a+b)
}
print(answer)

// 힙에 원소 추가
// 1. 배열 맨 끝에 추가
// 2. 끝 인덱스 값을 시작으로 부모값과의 대소 비교 후, 부모값보다 작다면 스왑 수행
// 3. (2)번 작업을 idx가 1까지 줄어들기 전까지 진행함. 단, 그 이전에 부모값보다 큰 경우엔 탈출
func push(_ n:Int) {
    heap.append(n)
    if(heap.count == 2) { return }

    var idx = heap.count-1
    while(idx > 1) {
        let parent = idx/2
        if(heap[parent] <= heap[idx]) { break }
        heap.swapAt(parent, idx)
        idx = parent
    }
}

// 최소힙의 값 삭제(루트 삭제) 및 반환
// 1. 힙의 맨 끝의 값을 루트로 이동
// 2. 갱신된 루트값에 대하여 자식값 두 개와 비교 후, 더 작은 자식 값이 있는 경우 스왑 수행
// (2)번 작업을 자식 인덱스값이 배열 범위 내에 있는 경우에 한하여 진행. 단, 두 자식보다 부모의 값이 작은 경우엔 탈출
func pop() -> Int {
    if(heap.count == 1) { return 0 }

    let deleted = heap[1]
    heap[1] = heap[heap.count-1]
    heap.removeLast()

    var idx = 1
    while(idx*2 < heap.count) {
        let left = idx*2, right = idx*2+1
        var minIdx = left
        if(right < heap.count && heap[right] < heap[left]) { minIdx = right }

        if(heap[idx] < heap[minIdx]) { break }
        heap.swapAt(idx,minIdx)
        idx = minIdx
    }
    return deleted
}

문제 URL

 

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

 

 

문제 설명 요약

  • 최소힙을 활용하여 주어지는 연산 처리
  • 단, 최소힙의 우선순위는 "절댓값이 작은 것, 절댓값이 같다면 값이 작은 것"

 

문제 풀이

  • 최소힙 직접 구현 필요 (스위프트는 힙 자료구조 없음)
    • Heap :: push -> 추가되는 값을 힙 내 원소들과 비교하여 저장 
    • Heap :: pop -> 최소힙의 값(최상단 루트 값) 삭제 및 반환

 

Swift를 활용한 풀이

var heap = [0] // 1-based-index
let N = Int(readLine()!)!
for _ in 0..<N {
    let x = Int(readLine()!)!
    if(x == 0) { print(pop()) }
    else { push(x) }
}

// 힙에 원소 추가
// 1. 배열 맨 끝에 추가
// 2. 끝 인덱스 값을 시작으로 부모값과의 대소 비교 후, 부모값보다 작다면 스왑 수행
// 3. (2)번 작업을 idx가 1까지 줄어들기 전까지 진행함. 단, 그 이전에 부모값보다 큰 경우엔 탈출
func push(_ n: Int) {
    heap.append(n)
    if(heap.count == 2) { return }

    var idx = heap.count-1
    while(idx > 1) {
        let parent = idx/2
        if(abs(heap[idx]) > abs(heap[parent])) { break }
        else if(abs(heap[idx]) == abs(heap[parent]) && heap[idx] > heap[parent]) { break }
        heap.swapAt(idx,parent)
        idx = parent
    }
}

// 최소힙의 값 삭제(루트 삭제) 및 반환
// 1. 힙의 맨 끝의 값을 루트로 이동
// 2. 갱신된 루트값에 대하여 자식값 두 개와 비교 후, 더 작은 자식 값이 있는 경우 스왑 수행
// (2)번 작업을 자식 인덱스값이 배열 범위 내에 있는 경우에 한하여 진행. 단, 두 자식보다 부모의 값이 작은 경우엔 탈출
func pop() -> Int {
    if(heap.count == 1) { return 0 }

    let deletedValue = heap[1]
    heap[1] = heap[heap.count-1]
    heap.removeLast()

    var idx = 1
    while(2*idx < heap.count) {
        let left = 2*idx, right = 2*idx+1
        var minIdx = left
        if(right < heap.count && abs(heap[right]) < abs(heap[left])) { minIdx = right }
        else if(right < heap.count && abs(heap[right]) == abs(heap[left]) && heap[right] < heap[left]) { minIdx = right }

        if(abs(heap[idx]) < abs(heap[minIdx])) { break }
        else if(abs(heap[idx]) == abs(heap[minIdx]) && heap[idx] < heap[minIdx]) { break }
        
        heap.swapAt(idx,minIdx)
        idx = minIdx
    }
    return deletedValue
}

 

문제 URL

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

 

문제 설명 요약

  • 최소힙을 활용하여 주어지는 연산 처리

 

문제 풀이

  • 최소힙 직접 구현 필요 (스위프트는 힙 자료구조 없음)
    • Heap :: push -> 추가되는 값을 힙 내 원소들과 비교하여 저장 (부모는 자식 두 개보다 값이 작아야 함)
    • Heap :: root -> 최소힙의 최상단 루트 값 조회
    • Heap :: pop -> 최소힙의 값 삭제 (최상단 루트 값의 삭제)

 

Swift를 활용한 풀이

 

var heap = [-1] // 1-based index

let N = Int(readLine()!)!
for _ in 0..<N {
    let x = Int(readLine()!)!
    if x == 0 {
        print(root())
        pop()
    } else {
        push(x)
    }
}

// 힙에 원소 추가
// 1. 배열 맨 끝에 추가
// 2. 끝 인덱스 값을 시작으로 부모값과의 대소 비교 후, 부모값보다 작다면 스왑 수행
// 3. (2)번 작업을 idx가 1까지 줄어들기 전까지 진행함. 단, 그 이전에 부모값보다 큰 경우엔 탈출
func push(_ n: Int) {
    heap.append(n)

    var idx = heap.count - 1
    while(idx > 1) { // 반복문 내에서 부모값을 조회하고자, /2를 수행하기 때문에 1보다 커야 함
        let parent = idx / 2
        if heap[parent] <= heap[idx] { break }
        heap.swapAt(parent, idx)
        idx = parent
    }
}

// 힙의 최상단 값 조회
func root() -> Int {
    return heap.count > 1 ? heap[1] : 0
}

// 최소힙의 값 삭제(루트 삭제)
// 1. 힙의 맨 끝의 값을 루트로 이동
// 2. 갱신된 루트값에 대하여 자식값 두 개와 비교 후, 더 작은 자식 값이 있는 경우 스왑 수행
// (2)번 작업을 자식 인덱스값이 배열 범위 내에 있는 경우에 한하여 진행. 단, 두 자식보다 부모의 값이 작은 경우엔 탈출
func pop() {
    if heap.count <= 1 { return } // 빈 힙 예외 처리

    heap[1] = heap[heap.count - 1] // 마지막 요소를 루트에 복사
    heap.removeLast() // 마지막 요소 제거(제거하지 않으면 불필요한 끝값이 지속적으로 쌓여 더미가 커짐)

    var idx = 1
    while(2*idx < heap.count) {
        let left = 2 * idx
        let right = left + 1
        var minIdx = left

        if(right < heap.count && heap[right] < heap[left]) { minIdx = right }

        if heap[idx] <= heap[minIdx] { break }
        heap.swapAt(idx, minIdx)
        idx = minIdx
    }
}

 

 

문제 URL

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

 

 

문제 설명 요약

  • 1부터 N까지의 숫자를 오름차순으로 적어둠
  • 적어둔 숫자 리스트에서 몇개 지워버림
  • 주어진 숫자는 지워지고 남은 숫자들을 하나로 이은 숫자
/// 예시 (입력 : 234092 / 출력 : 20)
예상할 수 있는 원본 숫자 리스트 : 2 / 3 / 4 / 10 / 19 / 20
N의 최솟값 : 20

 

 

문제 풀이

  • input : 입력 데이터를 쪼갠 배열 (각 원소를 '조각 숫자' 라고 부름)
  • cur : 원본 숫자 배열 내 포함되는지 판별하는 대상 숫자
  • 1씩 증가하는 cur 변수를 자릿수마다 쪼갠 배열을 탐색하며
    -> 각 자릿수 중 조각 숫자와 같은지 판별하고
    -> 같다면 인덱스 1 증가 후, 다음 반복으로 넘어가기 전 인덱스의 유효성 판단
  • 가능한 가장 작은 숫자로 조각 숫자를 포함하는 값을 찾아나가는 방식

 

Swift를 활용한 풀이

let input = readLine()!.map{Int(String($0))!}
var cur = 0
var idx = 0

outer: while(true) {
    cur += 1
    let list = String(cur).map{Int(String($0))!}
    for s in list {
        if(input[idx] == s) { 
            idx += 1
            if(idx >= input.count) {
                print(cur)
                break outer
            }
        }
    }
}

 

Java를 활용한 풀이

import java.util.*;
import java.lang.*;
import java.io.*;

class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        char[] input = br.readLine().toCharArray();
        int len = input.length;
        int cur = 0;
        int idx = 0;
        
        outer: while(true) {
            cur++;
            char[] cur_list = Integer.toString(cur).toCharArray();
            for(char c : cur_list) {
                if(c == input[idx]) {
                    idx++;
                    if(idx >= len) {
                        System.out.println(cur);
                        break outer;
                    }
                }
            }
        }
    }
}

 

문제 URL

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

 

 

 

문제 설명 요약

  • n개의 네모 모양 벽돌 정보가 주어짐
  • 주어진 정보는 '밑면의 넓이'와 '높이' 그리고 '무게'
  • 구해야 하는 것은 주어진 벽돌들 중 높이가 최대가 되도록 쌓을 때, 쓰인 벽돌의 개수와 각 벽돌의 순서
    (단, 순서를 출력할 때에는 제일 위에 쌓인 벽돌의 순서부터 출력해야함)
  • 벽돌을 쌓을 때의 규칙은 아래와 같음
    1. 벽돌은 회전시킬 수 없다. 즉, 옆면을 밑면으로 사용할 수 없다.
    2. 밑면의 넓이가 같은 벽돌은 없으며, 또한 무게가 같은 벽돌도 없다.
    3. 벽돌들의 높이는 같을 수도 있다.
    4. 탑을 쌓을 때 밑면이 좁은 벽돌 위에 밑면이 넓은 벽돌은 놓을 수 없다.
    5. 무게가 무거운 벽돌을 무게가 가벼운 벽돌 위에 놓을 수 없다

문제 풀이

 

  • 벽돌을 쌓을 수 있을지 없을지를 결정하는 요소가 '밑면의 넓이'와 '무게'이므로, 우선 둘 중에 하나를 기준으로 배열을 정렬해둠
  • 정렬 이후 Dynamic Programming(동적 계획법)을 활용하여, 쌓을 수 있는지 없는지에 대한 판단을 요소별로 쪼개어 수행

 

 

Swift를 활용한 풀이

 

import Foundation

let n = Int(readLine()!)! // 벽돌의 개수
var origin:[[Int]] = [] // 벽돌들의 정보

// 벽돌 정보 입력 받기
for _ in 0..<n {
    origin.append(readLine()!.split(separator: " ").map{Int(String($0))!})
}
// 벽돌의 밑면의 넓이를 기준으로 내림차순 정렬
var sorted_list = origin.sorted { $0[0] > $1[0] }

var dy:[Int] = [] // 높이합 최대 기록 배열
var hist:[Int:[Int]] = [:] // 각 항목 별 쌓을 수 있는 벽돌의 무게 정보 - [s:[w1, w2, ...]]

// 밑면이 가장 넓은 벽돌(맨 앞의 벽돌)에 대한 정보는 기본적으로 저장 (비교 대상이 없기 때문)
dy.append(sorted_list[0][1])
hist[sorted_list[0][0]] = [sorted_list[0][2]]

// 최대값에 관한 정보는 모두 밑면이 가장 넓은 벽돌(맨 앞의 벽돌)의 값으로 초기화
// (0과 빈 배열로 초기화해두면, 0번째 값을 고려하는 경우가 없어지기 때문)
var max_h_sum = dy[0] // 높이 합의 최대값
var max_h_list:[Int] = hist[sorted_list[0][0]]! // 높이 합이 최대인 경우의 높이값 요소 배열

// 2번째 벽돌 정보부터 살펴보면서, 이전 벽돌 중 자신이 위에 쌓일 수 있을지를 판단
for i in 1..<n {
    var max_h = 0
    var temp:[Int] = []
    for j in (0..<i).reversed() {
        // 이전 벽돌 중 자신보다 무게가 무거운 벽돌이면서, 높이의 합이 최대치인 경우의 정보를 임시 저장
        if (sorted_list[i][2] < sorted_list[j][2] && dy[j] > max_h) {
            max_h = dy[j]
            temp = hist[sorted_list[j][0]]!
        }
    }
    // 바로 위 for문에 의해 결정된 '높이의 합이 최대이면서, 무게가 자기자신보다 무거운 벽돌'의 정보를 기반으로 dy와 hist 초기화
    temp.append(sorted_list[i][2])
    hist[sorted_list[i][0]] = temp
    dy.append(max_h + sorted_list[i][1])

    // dy와 hist의 정보가 추가될 때마다, 각 높이의 합과 그것을 이루는 높이 요소값 배열을 갱신
    if (max_h_sum < dy[i]) {
        max_h_sum = dy[i]
        max_h_list = hist[sorted_list[i][0]]!
    }
}

// 결정된 최대 높이값의 요소 배열을 뒤집고 출력
// (무게를 기준으로 내림차순 된 배열에서 값을 찾았기때문)
max_h_list = max_h_list.reversed()
print(max_h_list.count)
for w in max_h_list {
    for i in 0..<origin.count {
        if(origin[i][2] == w) { print(i+1) }
    }
}

 

1. UIKit를 사용하는 새 프로젝트 생성

2. Main.storyboard 파일 삭제

3. info.plistStoryboard Name 제거

 

4. 프로젝트 Target 내 Build Setting UIKit Main Storyboard File Base Name 제거

 

5. SceneDelegate.swift 파일 내 willConnectTo 메소드 내용 수정

* UINavigationController(rootViewController: MainViewController()) 중 'MainViewController'는 처음 시작할 ViewController명으로 지정

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: windowScene)
        window?.rootViewController = UINavigationController(rootViewController: MainViewController())
        window?.backgroundColor = .white
        window?.makeKeyAndVisible()
    }
    
}

 

문제 링크 - https://www.acmicpc.net/problem/15649

 

15649번: N과 M (1)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해

www.acmicpc.net


문제 설명

n, m이 주어지면 1이상 n이하의 수 중 m개를 선택해 구성한 순열 출력

n >= m이며, n과 m 모두 1이상 8이하의 정수

 

풀이법 구상

파이썬을 사용하면 itertools 내 permutations 메소드를 써서 바로 풀이가 가능하지만 스위프트는 그런거 없음 왜..왜...

처음에는 1부터 n까지의 정수가 담긴 배열을 돌면서 하나씩 추가하고 배열에서 삭제해나가는 로직으로 생각했는데, 이러면 m중 반복문을 돌아야함 (그래도 시간복잡도가 O(N^m)이라 시간초과는 안 나겠지만 구현하기 까다로울 것 같아서 일단 포기)

 

1이상 n이하 정수 중에서 m개를 뽑는 방법은 DFS/BFS를 사용할 수 있는데

n이 4이고, m이 2인 경우를 생각해보면 1부터 4사이의 정수 중에 2개를 선택해야함

선택된 수를 담을 배열은 아래와 같은 경우의 수들을 가질 수 있음

 

[ ] -> [1] -> [1,2]

[ ] -> [1] -> [1,3]

[ ] -> [1] -> [1,4]

[ ] -> [2] -> [1,2]

[ ] -> [2] -> [2,3]

[ ] -> [2] -> [2,4]

...

[ ] -> [4] -> [3,4]

 

위 경우들은 트리로 시각화 할 수 있고, 각 노드들을 탐색하면서 노드를 구성하는 정수배열의 크기가 m인 경우만 중복없이 출력시키면 됨

 

는 그나마 익숙한 BFS로 구현했더니 시간초과

/// 시간초과난 코드

import Foundation

func solution(_ n: Int, _ m: Int) -> Void {
    var results: [[Int]] = []
    for i in 1...n { results.append([i]) }

    // 수열 생성
    for _ in 0..<m {
        for arr in results {
            var temp: [[Int]] = []
            for i in 1...n {
                if arr.contains(i) { continue }
                var temp_arr = arr
                temp_arr.append(i)
                temp.append(temp_arr)
            }
            
            for t in temp {
                if !results.contains(t) { results.append(t) }
            }
        }
    }

    // 사전 순으로 정렬
    var sorted_results: [[Int]] = []
    for arr in results { if (arr.count == m) { sorted_results.append(arr) } }
    
    sorted_results = sorted_results.sorted(by: { $0[0] < $1[0] })

    // 정답 출력
    for arr in sorted_results {
        var str = ""
        for a in arr { str += "\(a) " }
        print(str)
    }
}

let input = readLine()!.components(separatedBy: " ")
solution(Int(input[0])!, Int(input[1])!)

 

 

구글링했더니 대부분 백트래킹으로 풀었음

백트래킹은 DFS와 비슷하지만 DFS는 종료 조건만 가지고 무지성 재귀함수를 사용하고, 백트래킹은 답이 있을 것 같은 경우에만 탐색하는 것이 차이점임

백트래킹에서 쓰이는 '유망함', '유망하지않음', '가지치기'의 용어도 정답 존재 가능성과 연관된 것임

- 유망함 : 정답을 찾을 수 있는 가능성 O

- 유망하지않음 : 정답을 찾을 가능성 X

- 가지치기 : 유망하지 않은 경우를 제외하는 것

 

(아무튼 다시 이 문제로 돌아와서) BFS 풀이는 배열의 수가 0개 -> 1개 -> 2개 -> ... -> m개인 순서로 노드들을 탐색하기 때문에, 배열의 길이가 0개부터 m-1개인 탐색할 필요가 없는 경우까지 고려하게 됨

그리고 모든 결과 배열을 다 포함하고 마지막에 탐색하면서 길이가 m인 배열을 사전순으로 출력하기 위해 따로 정렬도 해줘야 함

근데 백트래킹 풀이는 가장 적은 숫자부터 구성된 배열 중, 길이가 m인 경우에만 출력하게 하면 되니까 불필요한 경우의 고려와 정렬 코드가 삭제될 수 있음

 

그럼 백트래킹을 쓰려면 뭐가 유망한 거고, 뭐가 유망하지 않은 건지를 알아야 하는데 n = 4, m = 2인 경우를 예시로 보면, 

결과배열이 [1]인 경우에는 1부터 4까지 중 1을 제외하고 추가해야함

이런 생각들로 아래처럼 구현하고 제출했더니 성공,,

 

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ n: Int, _ m: Int) -> Void {
    let temp:[Int] = []
    dfs(arr: temp, m: m, n: n)
}

func dfs(arr: [Int], m: Int, n: Int) -> Void {
    if (arr.count == m) {
        var str = ""
        for a in arr { str += "\(a) " }
        print(str)
        return
    }

    var temp = arr
    for p in 1...n {
        if !temp.contains(p) {
            temp.append(p)  // 1부터 n 사이의 배열에 없는 숫자 중 젤 작은 거 추가
            dfs(arr: temp, m: m, n: n)   // 위에서 숫자 새로 추가한 배열로 다시 검사
            temp.remove(at: temp.count-1)   // 위에서 검사 끝나고 오면 가장 최근에 추가했던거 삭제(그 자리에 다른 새로운거 넣어보려고)
        }
    }
}

let input = readLine()!.components(separatedBy: " ")
solution(Int(input[0])!, Int(input[1])!)

 

 

DFS/BFS로 풀 수 있을 것 같아도 시간 복잡도 한 번 더 고려해보고, 뭔가 될 것 같아서 풀었는데 시간초과나면 백트래킹 풀이도 고려해보자

 

 

문제 링크 - https://school.programmers.co.kr/learn/courses/30/lessons/43165

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr


문제 설명

주어진 정수 배열을 적절히 더하거나 뺀 값이 주어진 타겟 넘버와 같은 경우의 수를 출력

 

풀이법 구상

처음에는 이중 반복문을 돌면서 각 숫자를 음수로 바꾼 후의 총 합이 타겟과 같은지 판별하는 로직으로 구현했다가 실패

-> +4-1+2-1 과 같이 음수 사이에 양수가 존재하는 경우는 고려할 수 없음

 

각 숫자가 양수이거나 음수인 경우만 고려하면 되고 그러면 2^N의 시간 복잡도가 발생하는데, N의 최대값은 20으로 약 1,050,000이 됨

각 숫자가 양수이거나 음수가 되는 경우를 모두 고려하려면 각 숫자에 +,-를 붙인 노드로 이루어진 트리로 시각화할 수 있음

각 노드를 탐색하며 더한 합계값을 타겟 넘버와 비교하면 결과값 도출 가능

탐색에는 BFS/DFS를 모두 사용할 수 있으나 제출 코드는 BFS사용

 

 

코드 구현 (스위프트 사용)

import Foundation

func solution(_ numbers:[Int], _ target:Int) -> Int {
    var result = 0
    var sum_arr = [0]   // 반복문 시작을 위해 0 하나 추가
    
    for n in numbers {
        var temp: [Int] = []
        for s in sum_arr {
            temp.append(s+n)
            temp.append(s-n)
        }
        sum_arr = temp
    }
    
    for s in sum_arr { result += (s == target) ? 1 : 0 }
    
    return result
}

+ Recent posts