거북이의 IT 공부

[백준 15649] N과 M(1) - C++ / 백트래킹, 재귀 알고리즘 본문

Baekjoon

[백준 15649] N과 M(1) - C++ / 백트래킹, 재귀 알고리즘

버니빈 2020. 5. 15. 02:20

문제

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

 

15649번: N과 M (1)

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

www.acmicpc.net

백트래킹이란?

아래에 정리해 두었다!

https://bunnnybin.tistory.com/entry/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Backtracking-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%B4%EB%9E%80

 

백트래킹 (Backtracking) 알고리즘이란?

백트래킹이란? 해를 찾아가는 도중, 지금의 경로가 해가 될 것 같지 않으면 그 경로를 더이상 가지 않고 되돌아가는(Backtracking) 기법을 말한다. -> 반복문의 횟수를 줄여서 효율적이다!!! 좀더 정��

bunnnybin.tistory.com

순열구현 - 남의 코드로 공부해보기

수열 = 순열(순서가 있음), 조합(뽑기)

일단 백트래킹을 하기 전에 DFS를 구현하는 재귀함수를 공부할 필요가 있다.

"재귀를 통한 순열 구현"에 대해 공부해보자. 

* 참고사이트 :  https://yabmoons.tistory.com/100

 

[ 순열과 조합 구현 ] - 재귀를 통한 구현(2 - 순열) (C++)

이번 글은 저번 글에 이어서 순열에 대한 설명이다 ! ( 저번 글 바로가기 ) 기본적인 설명은 지난 글에서 모두 했으니 이번 글에서는 바로 순열 구현하는 것으로 들어가도록 하겠다. [ 구현방법 ]

yabmoons.tistory.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include<vector>
#define MAX 5
using namespace std;
 
int arr[MAX];
bool visited[MAX];
vector<int> vec;
 
void print() {
    for (int i = 0; i < vec.size(); i++)
        cout << vec[i] << " ";
    cout << '\n';
}
 
void dfs(int cnt) {  //cnt == count약자
    if (cnt == 3) {
        print();
        return;
    }
 
    for (int i = 0; i < MAX; i++) {
        if (visited[i] == truecontinue;
        visited[i] = true;
        vec.push_back(arr[i]);
        dfs(cnt + 1);
        vec.pop_back();
        visited[i] = false;
    }
}
 
int main(void) {
    arr[0= 1;
    arr[1= 2;
    arr[2= 3;
    arr[3= 4;
    arr[4= 5;
 
    dfs(0);
}
cs

<출력결과>

1 2 3
1 2 4
1 2 5
1 3 2
1 3 4
1 3 5
1 4 2
1 4 3
1 4 5
1 5 2
1 5 3
1 5 4   .....

 

<이해>

줄은 잘 이어져있다고 상상해주세요....(잘못 저장됨;;;)

1. cnt=0, i=0

arr = {1}

2. cnt = 1, i=0,1 

arr = {1,2}

3. cnt = 2, i=0,1,2

arr = {1,2,3}

4. cnt = 3, 출력

이 사진을 통해 dfs가 재귀함수로 구현됨을 확인할 수 있다!!

5. "백트래킹"

  cnt = 2, i = 2

  vec.pop_back(), visited[2] = false

  arr = {1, 2}

6. cnt = 2, i = 3

   arr = { 1, 2, 4 }

7. cnt = 3, 출력

 

8. cnt = 2, i = 3

  vec.pop_back(), visited[3] = false

  arr = {1, 2} 

9. cnt = 2, i = 4

   arr = {1,2,5}

10. cnt = 3, 출력

-> cnt = 2, i = 4 

     vec.pop_back(), visited[4] = false

 

 

★ ★ ★ ★중요 ★ ★ ★ ★

★중요

11. for문 다 돌아서 cnt = 1, i = 1

     vec.pop_back(), visited[1] = false

12. cnt = 1, i = 2

    arr = {1, 3}

 

13. cnt = 2, i = 0, 1

    arr = {1,3,2}

14. cnt = 2, i = 1

    vec.pop_back(), visited[1] = false

15. cnt = 2, i = 2, 3

    arr = {1,3,4}

 

이런 방식으로 dfs는 재귀로 돌아가고

재귀로 순열을 구할 수 있다.

 

정답 코드

이제는 순열 구현을 통해 백준 문제를 쉽게 풀 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <vector>
# define MAX 8
 
using namespace std;
 
int arr[MAX]; //주어진 숫자
bool visited[MAX]; //방문, 중복 방지
vector<int> vec; //출력할 수열
int cnt, n, m;
 
void Print() {
    for (int i = 0; i < vec.size(); i++)
        cout << vec[i] << " ";
    cout << '\n';
    return;
}
 
void DFS(int cnt) {
    if (cnt == m) {
        Print();
        return;
    }
 
    for (int i = 0; i < n; i++) {
        if (visited[i]) continue;
        visited[i] = true;
        vec.push_back(arr[i]);
        DFS(cnt + 1);
        vec.pop_back();
        visited[i] = false;
    }
}
 
int main(void) {
    cin >> n >> m;
 
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
        visited[i] = false;
    }
 
    DFS(0);
}
cs

 

Comments