문제

N과M(5) 백준 15654번

리뷰

1부터 N까지의 자연수가 아니라, 입력받은 N개의 수열을 이용한다.

중복을 허용하지 않고 2개 고르는 것이라서 중복체크를 위해 ch [10] 배열이 필요하다.

수열은 사전 순으로 증가하는 순으로 출력해야 하니까, 입력받자마자 sort() 했다.

코드

#include <iostream>
#include <algorithm> 
using namespace std;

// n과 m  (5)

int N, M; // 1부터 N까지의 자연수 중에서 M개를 고른 수열.  
int arr[10]; // 입력받은 수열 저장.
int ch[10]; // 중복을 허용하지 않으니까, 체크 배열이 필요하다.
int answer[10];

void go(int index){

    if(index == M){
        for(int i = 0; i < M; i++){
            printf("%d ", answer[i]);
        }
        cout << '\n';
        return;
    }

    for(int i = 0; i < N; i++){
        if(ch[i]) continue;

        ch[i] = 1;
        answer[index] = arr[i];
        go(index+1);
        ch[i] = 0;
    }
}

int main(void){

    cin >> N >> M;

     for(int i = 0; i < N; i++){
         scanf("%d", &arr[i]);
    }

    sort(arr, arr+N);

     go(0);

    return 0;
}

문제

N과M(6) 백준 15655번

리뷰

수열의 중복을 허용하지 않고, 사전 순으로 증가하는 순서로 출력해야 한다.

수열은 오름차순이어야 하므로 go 함수에 start 변수를 넣어서 이전 인덱스 보다 큰 인덱스부터 수열의 숫자를 고르도록 했다.

코드

#include <iostream>
#include <algorithm> 
using namespace std;

// n과 m  (6)
// 중복 허용 안 함. 오름차순 수열.

int N, M; // 1부터 N까지의 자연수 중에서 M개를 고른 수열.  
int arr[10];
int ch[10];
int answer[10];

void go(int index, int start){

    if(index == M){
        for(int i = 0; i < M; i++){
            printf("%d ", answer[i]);
        }
        cout << '\n';
        return;
    }

    for(int i = start; i < N; i++){
        if(ch[i]) continue;

        ch[i] = 1;
        answer[index] = arr[i];
        go(index+1, i+1);
        ch[i] = 0;
    }
}

int main(void){

    cin >> N >> M;

     for(int i = 0; i < N; i++){
         scanf("%d", &arr[i]);
    }

    sort(arr, arr+N);

     go(0, 0);

    return 0;
}
728x90

'알고리즘 > 백준' 카테고리의 다른 글

주사위 세개 c++ 백준 2480  (0) 2021.11.19
BFS 스페셜 저지 백준 16940 c++  (0) 2020.11.05
알고스팟 1261번 백준 c++  (0) 2020.10.22
부등호 백준 2529번 c++  (0) 2020.10.21
부분수열의 합 백준 1182번  (0) 2020.10.20

문제

알고스팟 백준 1261번

리뷰

"맞았습니다!" 를 보고 나서야 후련했다.

자꾸 틀렸던 이유는 boundary_check 함수에서 x는 N보다 작고, y는 M보다 작은지 확인해야 하는데,

y도 N보다 작다고 코딩해서 계속 틀렸었다....

deque를 써서 벽을 부순 경우에는 push_back , 빈 방에 온 경우 벽은 안부숴도 되니까 push_front 로 해결했다.

방문 표시를 하는 배열을 써도 되고, 안써도 된다.

구글링을 해보면 다익스트라를 쓴 코드도 있었다.

그리고 배운 점은, 정수1개를 입력 받을 때는 1d를 쓴다는 것이다.

for(int i = 1; i <= N; i++){     //map 입력받기
    for(int j = 1; j <= M; j++){
        scanf("%1d", &map[i][j]);  // 정수 1개를 입력 받을 때는 1d 를 쓴다. 
        broken_cnt[i][j] = -1;  // 전부 -1로 초기화 
    }
} 

변수명, 함수명도 약어 없이 짓고 누구나 이해할 수 있도록 직관적이고 자세하게 적는게 좋다는데.

알고리즘 문제를 풀 때는 자꾸 짧게만 쓰게 된다. 마음이 급해서 그런가보다.

백준 문제 중에 BFS 가 필요한 숨바꼭질, 숨바꼭질3을 풀어본게 도움이 됬던 것 같다.

코드 1

main() 함수는 최대한 간결해야 한다는 글을 읽어서 Input 과 Solve 로 코드를 분리해 보았다.

#include <iostream>
#include <algorithm> 
#include <vector>
#include <deque>
#include <utility>
#define MAX 101
using namespace std;

int N, M, answer;  // 세로 N, 가로M

int map[MAX][MAX]; // 지도  
int broken_cnt[MAX][MAX] = {-1, }; // // 현재 지점에서 벽을 부순 횟수
bool visit[MAX][MAX]; // 방문 여부 

int xx[5] = {0, 0, -1, 1};
int yy[5] = {-1, 1, 0, 0};

deque<pair<int, int> > de;

bool check_boundary(int x, int y){
    return (x <= N && x >= 1 && y <= M && y >= 1 );
}

void BFS(int x, int y){ // (1,1)에서 시작해서 (N,M)에 도착해야 한다.  

    de.push_back( {x, y});
    broken_cnt[x][y] = 0;
    visit[x][y] = true;

    while(!de.empty()){  

        int now_x = de.front().first;
        int now_y = de.front().second;
        de.pop_front();

        for(int i=0; i < 4; i++){

            int next_x = now_x + xx[i];
            int next_y = now_y + yy[i];

            if(check_boundary(next_x, next_y) == false) continue; // 좌표 범위 체크  

            if(true == visit[next_x][next_y]) continue; // 이미 방문했다면 지나간다. 

            if(map[next_x][next_y] == 1){  // 벽이면 뚫는다.  
                broken_cnt[next_x][next_y] = broken_cnt[now_x][now_y]+1; 
                visit[next_x][next_y] = true;
                de.push_back({next_x, next_y});

            }else if(map[next_x][next_y] == 0){ // 빈방이면 안 뚫어도 된다. 
                broken_cnt[next_x][next_y] = broken_cnt[now_x][now_y];
                visit[next_x][next_y] = true;
                de.push_front({next_x, next_y});

            }
        }

    }
}

void Solve(){
    BFS(1, 1);
     cout << broken_cnt[N][M];
}

void Input(){
    cin >> M >> N ; // 가로M, 세로 N

    for(int i = 1; i <= N; i++){     //map 입력받기
        for(int j = 1; j <= M; j++){
            scanf("%1d", &map[i][j]);  // 1개의 정수를 입력받을 때 1d 
         }
    } 
}

int main(void){

    Input();

    Solve();   

    return 0;
} 

코드2

#include <iostream>
#include <algorithm> 
#include <vector>
#include <deque>
#include <utility>
#define MAX 101
using namespace std;


int N, M, answer;
int map[MAX][MAX]; // 지도  
int broken_cnt[MAX][MAX] = {-1, }; // // 현재 지점에서 벽을 부순 횟수

int xx[5] = {0, 0, -1, 1};
int yy[5] = {-1, 1, 0, 0};

deque<pair<int, int> > de;

bool check_boundary(int x, int y){ // 좌표 범위 체크  
    return (x <= N && x >= 1 && y <= M && y >= 1 );
}

void BFS(){ // (1,1)에서 시작해서 (N,M)에 도착해야 한다.  

    de.push_back( {1, 1});
    broken_cnt[1][1] = 0; // 일단 방문하면 0 

    while(!de.empty()){  

        int now_x = de.front().first ;
        int now_y = de.front().second;
        de.pop_front();

        int now_cnt = broken_cnt[now_x][now_y]; // 현재 지점에서 벽을 부순 횟수  

        for(int i=0; i < 4; i++){
            int next_x = now_x + xx[i];
            int next_y = now_y + yy[i];

            if(check_boundary(next_x, next_y) == false) continue; // 좌표 범위 체크  

            if(broken_cnt[next_x][next_y] != -1) continue; // 이미 방문했다면 지나간다. 

            if(map[next_x][next_y] == 1){  // 벽이면 뚫는다.  
                broken_cnt[next_x][next_y] = now_cnt+1; 
                de.push_back({next_x, next_y});

            }else if(map[next_x][next_y] == 0){ // 빈방이면 안뚫어도 된다  
                broken_cnt[next_x][next_y] = broken_cnt[now_x][now_y];
                de.push_front({next_x, next_y});
            }
        }

    }
}

void Solve(){

    BFS();
     cout << broken_cnt[N][M];
}

void Input(){

    cin >> M >> N ; // 가로M, 세로 N

    for(int i = 1; i <= N; i++){     //map 입력받기
        for(int j = 1; j <= M; j++){
            scanf("%1d", &map[i][j]);  // 정수 1개를 입력 받을 때는 1d 를 쓴다. 
            broken_cnt[i][j] = -1;  // 전부 -1로 초기화 
        }
    } 
}

int main(void){

    Input();

    Solve();   

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

BFS 스페셜 저지 백준 16940 c++  (0) 2020.11.05
N과 M(5), (6) 백준 15654번 c++  (0) 2020.10.22
부등호 백준 2529번 c++  (0) 2020.10.21
부분수열의 합 백준 1182번  (0) 2020.10.20
외판원순회2 백준 10971번 c++  (0) 2020.10.20

문제

부등호 백준 2529번

리뷰

부등호의 개수k와 부등호 리스트가 주어진다.

< < < > < < > < >

k+1 개의 숫자를 나열하여 부등호를 만족시켜야 한다. 0부터 9까지의 숫자만 사용 가능하다.

이를 만족하는 수열 예시 2개는 아래와 같다. 수열은 모두 달라야 한다. 숫자를 모두 이어붙이면 하나의 '숫자'가 된다.

//  3456128790 뿐만 아니라 5689023174도 아래와 같이 부등호 관계 A를 만족시킨다. 
3 < 4 < 5 < 6 > 1 < 2 < 8 > 7 < 9 > 0
5 < 6 < 8 < 9 > 0 < 2 < 3 > 1 < 7 > 4

주어진 부등호 관계를 만족하는 k+1자리의 최소 정수와 최대 정수를 출력해야 한다.

0부터 9까지 10가지의 숫자를 중복하지 않고 수열을 만드는 것은 재귀를 이용하여 N과 M 시리즈 문제에서 해봤다.

아래는 기본 구조다. index는 채워야 할 자리의 index 이다.

만약 4자리 숫자로 구성된 수열을 만들어야 하면, (n == 4 라고 가정)

index가 0,1,2,3 까지 변화하면서 for문에서 숫자를 선택하여 채우고, ( 이 때, ch[i] 배열을 검사하면서 선택이 이미 된 )

index가 4가 된 순간에 if문 조건을 만족시키면서 수열이 완성되서 return 된다.

bool ch[11]; // 중복을 피하기 위한 체크 배열 

void Combination(int index){ 

    if(index == n ){ // 수열 완성. 
        //
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

         ch[i] = true; // 선택
         Combination(index+1); // 다음 자리수 선택을 위해 재귀 호출 
         ch[i] = false; // 선택 안함  
    } 
}

이 문제에서는 백트레킹 기법을 쓴다.

백트레킹은 의미 없는 함수 호출을 피하기 위해 앞에 조건문으로 검사해서 함수 호출 횟수를 줄이는 것이다.

k 개의 부등호가 주어지면, 수열의 숫자는 k+1개로 구성된다.

Combination 함수는 index 자리의 숫자를 선택하면서 숫자를 string num에 담아서 넘긴다.

만약 첫째 자리 숫자를 선택해야 하는 index == 0 인 상황에는, 무조건 숫자를 선택한다. 재귀 호출을 피할 이유가 없다.

이와 달리 index가 1 이상인 경우, 즉 둘째, 셋째 그 이상의 자리의 숫자를 선택해야 하는 상황일 때는 앞자리 숫자와 부등호를 고려해야 한다.

// 왜냐하면 첫째 숫자가 9 이고 부등호가 < 인 경우가 있다. 그런데 9 를 초과하는 1자리 숫자는 없다.

if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1]))

// 따라서 앞자리 숫자, 현재 선택하려는 숫자, 앞자리 부등호를 인자로 넘겨서 검사한다.
// 아래는 함수의 시그니처다.     
bool SignCheck(char a, char b, char oper); 

SignCheck 함수는 현재 선택하려는 숫자 b가 부등호를 만족시킬 수 있는 숫자라면 true를 반환한다.

수열이 완성되면 index가 k+1이 되는 순간이다. 그 때 ans 벡터에 넣는다.

답은 최대, 최소 숫자니까 ans 벡터를 정렬해서 맨앞, 맨뒤 값을 출력하면 된다.

void Combination(int index, string num){ 
    // index : 선택해야 할 (수열에서의) 숫자 자리. 0부터 9까지. 

    if(index == (k+1) ){ // 수열 완성. 벡터에 넣는다 
        ans.push_back(num); 
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

        // 첫번째 자리 숫자를 고르는 거라면, 무조건 선택. (index == 0)
        //  그게 아니라면, 앞자리 숫자 num[index-1] 과 현재 선택하려는 숫자 i 와 부등호를 넘긴다.  
        if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1])){
            ch[i] = true; // 선택
             Combination(index+1, num + to_string(i)); // 다음 자리수 선택을 위해 재귀 호출 
            ch[i] = false; // 선택 안함  
        }

    } 
}

코드

#include <iostream>
#include <algorithm> 
#include <vector>
#include <string>
using namespace std;


int k;
char sign[11];  // 부등호 담은 수열   
int num[11];     // 조합으로 만들어진 수열  
bool ch[11];  // 숫자 중복 불허 
vector<string> ans; // 부등호를 만족하는 수열을 저장하는 벡터 
string maxnum, minnum;

bool SignCheck(char a, char b, char oper){ // 부등호를 만족시키는지 검사

    if(oper == '<'){
        if(a>b) return false;

    } else {// '>'
        if(a<b) return false;
    }
    return true;
}

void Combination(int index, string num){ 
    // index : 선택해야 할 (수열에서의) 숫자 자리. 0부터 9까지. 

    if(index == (k+1) ){ // 수열 완성. 벡터에 넣는다 
        ans.push_back(num); 
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

        // 첫번째 자리 숫자를 고르는 거라면, 무조건 선택. (index == 0)
        //  그게 아니라면, 앞자리 숫자 num[index-1] 과 현재 선택하려는 숫자 i 와 부등호를 넘긴다.  
        if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1])){
            ch[i] = true; // 선택
             Combination(index+1, num + to_string(i)); // 다음 자리수 선택을 위해 재귀 호출 
            ch[i] = false; // 선택 안함  
        }
    } 
}


int main(void){

    cin >> k;

     for(int i = 0; i < k; i++){
         cin >> sign[i];
    } // 입력받기 끝 

    Combination(0, "");    

    sort(ans.begin(), ans.end()); // 오름차순 정렬 후, 맨 뒤가 최대, 맨 앞이 최소

    cout << ans[ans.size()-1] << '\n' << ans[0];

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

N과 M(5), (6) 백준 15654번 c++  (0) 2020.10.22
알고스팟 1261번 백준 c++  (0) 2020.10.22
부분수열의 합 백준 1182번  (0) 2020.10.20
외판원순회2 백준 10971번 c++  (0) 2020.10.20
N과 M (3),(4)  (0) 2020.10.20

문제

부분수열의 합 백준 1182번


리뷰

혼자 힘으로 못풀겠어서 다른 분들 코드를 봤다.

N개의 숫자를 입력받아서 합이 S가 되는 부분 집합의 개수를 구하는 문제다.

부분 집합을 만들 줄 알아야 한다.


인덱스 0부터 N-1 까지 순회하면서, 현재 인덱스의 숫자를 집합에 '포함' / '미포함' 을 구분하여 재귀호출한다.

탐색 함수의 시그니처는 현재 index를 나타내고, 공집합이면 안되니까 포함시킨 숫자의 cnt를 세준다.

void dfs(int index, int sum, int cnt)

코드

#include <iostream>
#include <algorithm> 
using namespace std;


int N, S, answer;
int a[21];

void dfs(int index, int sum, int cnt){

    if(index == N){
        if(S == sum && cnt > 0) {
            answer++;
        }
        return;
    }

    dfs(index+1, sum+a[index], cnt+1); // 포함한다.
    dfs(index+1, sum, cnt); // 포함 안한다  
} 

int main(void){

    cin >> N >> S;

    for(int i = 0; i < N; i++){
        scanf("%d", &a[i]);
    } // 수열의 수를 입력받는다. 

    dfs(0, 0, 0);

    cout << answer;

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

알고스팟 1261번 백준 c++  (0) 2020.10.22
부등호 백준 2529번 c++  (0) 2020.10.21
외판원순회2 백준 10971번 c++  (0) 2020.10.20
N과 M (3),(4)  (0) 2020.10.20
N과 M(1), (2)  (0) 2020.10.20

문제

외판원 순회2 백준 10971번

리뷰

N개의 도시가 있다. 어떤 도시에서 시작해서 모든 도시를 방문하고 다시 시작 도시로 돌아오는 최소 비용을 구하는 문제.

완전탐색으로 풀었다.

N개 도시를 모두 시작점 삼아서 탐색해봐야 한다.

for문 하나면 되는데, 이중 for문으로 해서 틀렸었다...

탐색 함수의 시그니처는 아래와 같다.

void search(int visit_city, int cnt, int cost);
// visit_city는 현재 도시 번호
// cnt는 그동안 방문 도시 개수
// cost는 이동하면서 누적한 비용

cnt가 N개가 되는 순간, 현재 지점에서 '시작점' 으로 갈 수 있는지 확인하고, 이동 후 최소비용을 갱신한다.

search 함수 내부에 재귀호출이 있는 for문이 있다.

i와 j간에 경로가 없는 경우 m 배열 값이 0이니까 continue 처리한다.

이미 방문한 도시도 재방문하면 안되니까 continue 처리한다.

void search(int visit_city, int cnt, int cost){

    if(cnt == N ){ // 모든 도시 방문했고, 목적지 도착시 종료   
        if(m[visit_city][start_city] > 0){ // 시작점으로 돌아옴. 
            mcost = min(cost + m[visit_city][start_city], mcost);
        }
        return; 
    }

    for(int a = 0; a < N; a++){ 

        if(m[visit_city][a] == 0) continue; // 못가는 곳  
        if(visit_check[a] == true) continue; // 이미 방문한 도시 

        visit_check[a] = true; // 방문표시  
        search(a, cnt+1, cost + m[visit_city][a]); // 방문해본다  
        visit_check[a] = false; 
    }
}

코드1

#include <iostream>
#include <algorithm> 
using namespace std;

// 외판원순회2

int N;
int m[11][11];
int visit_check[11]; // 방문체크 
int mcost = 2e9;  // 최소비용 
int start_city; // 시작지점이자 목적지점

void search(int visit_city, int cnt, int cost){

    if(cnt == N ){ // 모든 도시 방문했고, 목적지 도착시 종료   
        if(m[visit_city][start_city] > 0){ // 시작점으로 돌아옴. 
            mcost = min(cost + m[visit_city][start_city], mcost);
        }
        return; 
    }

    for(int a = 0; a < N; a++){

        if(m[visit_city][a] == 0) continue; // 못가는 곳  
        if(visit_check[a] == true) continue; // 이미 방문한 도시 

        visit_check[a] = true; // 방문표시  
        search(a, cnt+1, cost + m[visit_city][a]); // 방문해본다  
        visit_check[a] = false; 
    }
}


int main(void){

    cin >> N; // 도시 개수 

    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            cin >> m[i][j];
        }    
    } // 입력받기 끝. 

    for(int i = 0; i < N; i++){

        start_city = i; // 시작 도시 번호 
        visit_check[i] = true; // 시작 도시 방문했으니까 cnt 1 로 시작 
        search(i, 1, 0); 
        visit_check[i] = false;
    }

    cout << mcost;

    return 0;
} 

코드2

next_permutation() 써서 풀 수 있다. 왜냐하면, 어떤 순서대로 도시를 방문해야 하는가 의 문제이기 때문에.

순열을 리턴해주는 함수로 해결가능하다.

도시 번호 수열을 만들기 위해 A [ ] 배열을 만든다. 1 2 3 ... N 의 숫자가 들어간다.

마지막 N번 도시에서 1번 도시로 (시작점으로) 오는 경로가 있는 경우 비용을 합산한다.

next_permu() 함수가 만들어주는 수열 대로 방문한다.

경로가 없으면 모든 도시를 방문할 수 없기 때문에 all_visit 플래그를 활용한다.

모든 경로를 방문한 경우에만 최소비용을 갱신한다.

#include <iostream>
#include <algorithm> 
using namespace std;

// 외판원 순회2 

int N;
int M[11][11];
int A[11]; // 도시 번호 


int main(void){

     freopen("input.txt", "rt", stdin);

    cin >> N;

    for(int i = 1; i <= N; i++){
        for(int j = 1; j <= N; j++){
            cin >> M[i][j];
        }
    } // 도시 간의 경로 입력받기 끝. 


    for(int i = 1; i <= N; i++){ // 도시에 번호 붙이기 (1부터 N까지) 
        A[i] = i;
    }

    int mincost = 2e9;

    do{
        int cost = 0;
        bool all_visit = true;
        int i = 1;

        for(i = 1; i <= N-1; i++){

            if(M[A[i]][A[i+1]] > 0){
                cost += M[A[i]][A[i+1]];
            }else{
                all_visit = false; // 경로가 없어서 실패.
                break;
            }
        }

        // 마지막 N번 도시에서 1번 도시로 (시작점으로) 오는 경로가 있는 경우 비용을 합산.
        if(M[A[N]][A[1]] > 0) cost += M[A[N]][A[1]];
        else all_visit = false; // 모든 도시 방문 실패.  

        if(all_visit){
            mincost = min(cost, mincost);
        }

    }while( next_permutation(A, A+N));

    cout << mincost;

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

부등호 백준 2529번 c++  (0) 2020.10.21
부분수열의 합 백준 1182번  (0) 2020.10.20
N과 M (3),(4)  (0) 2020.10.20
N과 M(1), (2)  (0) 2020.10.20
설탕배달 백준 2839번  (0) 2020.10.07

문제

N과M(3) 백준 15651번


리뷰

오름차순 수열을 출력하되, *숫자 중복을 허용한다. *

코드

#include <iostream>
#include <algorithm> 
using namespace std;

/*  n과m (3)
중복 허용
1부터 n까지 자연수 중에서 m개를 고른 수열  
*/ 

int n, m;
int arr[10]; // 출력할 수열을 담는다.  

void go(int index){  // index 0부터 m-1 까지 어떤 수로 채울지 선택하는 함수  

    if(index == m){ // index m-1까지 모든 수를 골랐으니까 수열 출력 

        for(int i = 0; i < m; i++){ // 수열 출력
            printf("%d ", arr[i]);
        }
        cout << '\n';        
        return; 
    } 

    for(int i = 1; i <= n; i++){ // 1부터 n까지 수를 '중복 허용'하여 선택
        arr[index] = i;
        go(index+1); // 다음 자리 숫자를 고르러 재귀호출 
    }

}

int main(void){

       scanf("%d %d", &n, &m);

      go(0); // 0번째 자리에 어떤 수를 넣을지 선택한다.  

    return 0;
} 

문제

N과M(4) 백준 15652번


리뷰

비내림차순 수열을 출력하되, *숫자 중복을 허용한다. *

비내림차순은 '같은 숫자가 연속되거나 오름차순'이다. 예를 들어, 11223 같은 수열이 비내림차순이다.

중복 허용이니까 중복체크할 배열이 필요 없다.


코드 1

#include <iostream>
using namespace std;


/*   n과m (4)
중복 허용, 비내림차순  
1부터 n까지 자연수 중에서 m개를 고른 수열  
*/ 

int n, m;
int arr[10]; // 출력할 수열을 담는다.  

void go(int index, int start_num){  

    if(index == m){ // 수열 출력
        for(int i = 0; i < m; i++){
            printf("%d ", arr[i]);
        }
        cout << '\n';
        return; 
    } 

    for(int i = start_num; i <= n; i++){ // 선택 범위의 시작 숫자만 정해준다. 
        arr[index] = i; 
        go(index+1, i); // 반드시 i 보다 큰 수부터 다시 선택해야 하는게 아니다.(비내림차순)
    }

}

int main(void){

       scanf("%d %d", &n, &m);

      go(0, 1); // 0번째 자리에 어떤 수를 넣을지 선택한다.  start는 선택범위의 시작숫자. 

    return 0;
} 

코드 2

m 개로 이루어진 배열 arr를 채우는 '완전탐색' DFS로 풀이했다.

#include <iostream>
using namespace std;

/*  n과m (4)  DFS로 푼 코드 
중복 허용, 비내림차순  
1부터 n까지 자연수 중에서 m개를 고른 수열  
*/ 

int n, m;
int arr[10]; // 숫자 m개를 골라 저장할 수열  

void dfs(int start_num, int cnt){ // 비내림차순 이니까 start_num 활용.  cnt는 선택한 숫자 개수

    if(cnt == m){ // 0 ~ m-1 까지 총 m 개 선택 완료해서 수열을 출력.
        for(int i = 0; i < m; i++)
            cout << arr[i] << ' ';
        cout << '\n';
        return;
    }

    for(int i = start_num; i <= n; i++){ // 비내림차순
        arr[cnt] = i;  // cnt자리에 i숫자를 넣는다. 선택한 것.
        dfs(i, cnt+1); 
        // 비내림차순이니까 i 부터 다시 선택할 수 있게 한다. 윗줄에서 선택했으니 cnt+1을 넘긴다. 
    }
} 


int main(void){

      scanf("%d %d", &n, &m);

      dfs(1, 0);  // 1부터 시작한다. 고른 개수.

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

부분수열의 합 백준 1182번  (0) 2020.10.20
외판원순회2 백준 10971번 c++  (0) 2020.10.20
N과 M(1), (2)  (0) 2020.10.20
설탕배달 백준 2839번  (0) 2020.10.07
분해합 백준 2231번 c++  (0) 2020.10.07

N과 M문제집

1부터 12까지 시리즈가 있는 N과 M 문제집

순열, 조합을 연습하는 N과 M 시리즈 문제다. 백준님께서 1~4번 문제가 매우 중요하다고 하셨다.

문제

N과M(1) 백준 15649번

리뷰

재귀 호출을 이용해서 N개중에 M개를 고른다.

M개를 고를 때, permu() 함수는 매개변수로 지금 '몇 번째' 숫자를 고른다는 기준을 잡는다.

int check[ ] 배열을 이용해 특정 숫자를 골랐는지/안골랐는지 표시한다.

숫자 중복을 허용하지 않기 때문에 선택 여부 표시를 위해 필요하다.

코드

#include <iostream>
using namespace std;

int N, M;
int check[10]; // 확인 여부  
int answer[10]; // 선택한 인덱스  

void permu(int idx){ // 'idx' 번째에 어떤 수를 놓을지 고른다. 

    if(idx > M){ // 이미 M개를 골랐으니까, M번째를 초과하는 순간 종료. 

        // 수열 출력 
        for(int p = 1; p <= M; p++){
            cout << answer[p] << ' ';
        }
        cout << '\n';

        return; 
    }

    for(int i = 1; i <= N; i++){ 

        if(check[i]) continue; // 이미 선택된 수는 지나간다 

        check[i] = 1; // 선택 함
        answer[idx] = i; // idx 자리에 숫자 i 배치 

        permu(idx+1); // 다음 자리의 숫자 고르러 재귀          

        check[i] = 0; // 선택 안함  
    }
}

int main(void){

    freopen("input.txt", "rt", stdin);

    cin >> N >> M;

    permu(1);

    return 0;    
}

문제

N과M(2) 백준 15650번

백준님이 강조하셨다 중요하다고.

리뷰

오름차순 수열을 출력하되, 숫자가 중복되지 않도록 한다.

즉, 이미 고른 숫자보다 큰 숫자부터 골라야 한다.

코드 1

#include <iostream>
#include <algorithm> 
using namespace std;

// N과 M (2)  

int N, M;
bool check[10];
int answer[10];

void go(int index, int start){

    if(index == M){
        // 수열 출력 
        for(int i = 0; i < M; i++){
            cout << answer[i] << ' ';
        } 
        cout << '\n';
        return;
    }

    for(int i = start; i <= N; i++){
        if(check[i]) continue; // 이미 선택된 숫자라면 지나간다. 

        check[i] = true;
        answer[index] = i; // index 자리에 i를 선택한다. 
        go(index+1, i+1); // index+1 자리에 (==다음 자리)숫자를 고르기 위해 재귀 호출.
        check[i] = false; 
    }

}

int main(void){

     scanf("%d %d", &N, &M);

    go(0, 1); // 0번째를 채우자.  

    return 0;
} 

코드2

선택 한다/ 안한다의 관점

함수 시그니처는 아래와 같다.

void go(int num, int selected_cnt);

// num 에는 1부터 n까지의 숫자가 들어온다.  1, 2, 3, ... n 
// selected_cnt는 0부터 m-1까지 숫자가 들어온다. 

1부터 n까지 수가 들어오는데, 이 숫자를 선택 할지, 말지를 선택한다.

selected_cnt 가 m이 되는 순간 수열을 출력한다.

선택 한다면, selected_cnt 자리에 num을 넣는다.

arr[selected_cnt] = num; // 선택함  
go(num+1, selected_cnt+1);

arr[selected_cnt] = 0; // 선택 안함  
go(num+1, selected_cnt);
#include <iostream>
using namespace std;

// n과 m (2)

int n, m;
int arr[10]; // num 숫자를 선택 한다/안한다를 표시  

// num 에는 1부터 n까지의 숫자가 들어온다. 
// selected_cnt는 0부터 m-1까지 숫자가 들어온다. 

void go(int num, int selected_cnt){ // num 숫자를 선택 한다/안한다 ,  선택한 수의 개수 

    if(selected_cnt == m){
        // 수열 출력
        for(int i = 0; i < m; i++){
            printf("%d ", arr[i]);
        }
        cout << '\n';

        return; 
    } 

    if(num > n) return; // n을 초과하면 리턴  

    arr[selected_cnt] = num; // 선택함  
    go(num+1, selected_cnt+1);

    arr[selected_cnt] = 0; // 선택 안함  
    go(num+1, selected_cnt);

}

int main(void){

       scanf("%d %d", &n, &m);

      go(1, 0); 

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

외판원순회2 백준 10971번 c++  (0) 2020.10.20
N과 M (3),(4)  (0) 2020.10.20
설탕배달 백준 2839번  (0) 2020.10.07
분해합 백준 2231번 c++  (0) 2020.10.07
소트인사이드 백준 1427번  (0) 2020.10.02

+ Recent posts