문제

부분수열의 합 백준 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

문제

설탕 배달 백준 2839번


리뷰

3kg 또는 5kg 봉지를 최소 개수로 활용해서 N kg을 맞추면 된다.

봉지의 개수가 최대일 때는 3kg 만 이용하는거니까 N / 3 개가 최대 개수가 된다.

즉, N =18kg 일 때, 18 / 3 = 6봉지가 최대 개수다.

그래서 3kg 을 0개 이용할 때 부터 6개 이용하는 경우까지 확인하도록 반복문을 만들었다.

int maxN = N/3;  

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

    int cnt = i; // 봉지 개수 
    int sum = N-(i*3); // 3kg 곱해서 N에서 뺀 kg 

    if(  sum % 5 == 0){
        cnt += (sum/5);
        min_cnt = min(cnt, min_cnt);
    }
}

3kg 봉지를 몇개 이용하고, 나머지 kg은 sum이 된다.

만약, 5kg 봉지로 sum이 나누어 떨어진다면, 정확하게 N kg을 맞출 수 있는 경우다!

여기서 min_cnt를 갱신할 수 있다.


마지막에서 최소 봉지 개수 min_cnt 가 여전히 2e9로 초기값과 같다면, -1을 출력한다.

    if(min_cnt == 2e9) cout << -1;
    else cout << min_cnt;

맞은 코드

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

int N;
int min_cnt = 2e9; // 최소 봉지 개수 

int main(void){

    scanf("%d", &N); // 목표 kg 

    int maxN = N/3;  

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

        int cnt = i; // 봉지 개수 
        int sum = N-(i*3); // 3kg 곱해서 N에서 뺀 kg 

        if( sum % 5 == 0){
            cnt += (sum/5);
            min_cnt = min(cnt, min_cnt);
        }
    }

    if(min_cnt == 2e9) cout << -1;
    else cout << min_cnt;

    return 0;
} 
728x90

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

N과 M (3),(4)  (0) 2020.10.20
N과 M(1), (2)  (0) 2020.10.20
분해합 백준 2231번 c++  (0) 2020.10.07
소트인사이드 백준 1427번  (0) 2020.10.02
수 정렬하기3 백준 10989번  (0) 2020.10.02

문제

분해합 백준 2231번

리뷰

N의 생성자를 찾으려면 1부터 N-1 까지 전부 탐색하면 된다.

N은 1이상 100만 이하의 수가 들어오니까 for문으로 찾아도 2초 시간제한을 초과하지 않는다.

각 자리의 수가 가질 수 있는 최대 숫자는 9이다.

 

N이 219 일 때, 219는 3 자리수이다.

219에서 9를 3번 빼면 219의 생성자의 최소값을 구할 수 있다. 219 - 9 -9 -9 = 192

따라서 192 부터 218까지 탐색해서 생성자를 구해도 된다. 1부터 탐색할 필요가 없다.

맞은 코드 1

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

int N, answer;

int Sum_total(int n){ 

    int sum = n; // 일단 자기 자신을 더한다. 
    while(n > 0){ 
        sum += (n%10);  // 각 자리수를 더한다 
        n /= 10;
    }
    return sum;
}

int main(void){

    cin >> N;

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

        int sum = Sum_total(i);

        if(sum == N) {
            answer = i;  // 생성자 찾음 
            break;
        }
    }    

    printf("%d", answer);  

    return 0;
} 

맞은 코드 2

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

int N, answer; // 생성자 못찾으면 answer = 0

int main(void){

    cin >> N;

    int tempN = N;
    int len = 0;

    while(tempN > 0){ // 1. N의 자리수 len에 구하기  
        tempN /= 10;
        len++;
    }

    int start_n = len*9; // 자리수 만큼 9를 뺸다 

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

        int sum = i;
        int temp = i;

        while(temp > 0){ // 2. 각 수의 자리수의 합  
            sum += (temp%10);
            temp /= 10;
        }

        if(sum == N) {
            answer = i; // 생성자 찾음 
            break;
        }
    }    

    printf("%d", answer);

    return 0;
} 
728x90

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

N과 M(1), (2)  (0) 2020.10.20
설탕배달 백준 2839번  (0) 2020.10.07
소트인사이드 백준 1427번  (0) 2020.10.02
수 정렬하기3 백준 10989번  (0) 2020.10.02
팰린드롬수 백준 1259번  (0) 2020.10.02

문제

소트인사이드 백준 1427번


리뷰

입력받은 수 N을 한 자리 숫자 씩 잘라서 벡터에 넣는다.

greater() 로 내림차순 정렬한다.

#include <algorithm> 
#include <functional>

sort(v.begin(), v.end(), greater<int>()); // 내림차순 정렬 

맞은 코드

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

int N, len;
vector<int> v;   

int main(void){

    scanf("%d", &N);

    while(N > 0){
        v.push_back(N % 10); // 한 개씩 벡터에 넣는다     
        N /= 10;
        len++;
    }

     sort(v.begin(), v.end(), greater<int>()); // 내림차순 정렬 

     for(int i = 0; i < len; i++){   
        printf("%d",v[i]);
    }

    return 0;
} 


728x90

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

설탕배달 백준 2839번  (0) 2020.10.07
분해합 백준 2231번 c++  (0) 2020.10.07
수 정렬하기3 백준 10989번  (0) 2020.10.02
팰린드롬수 백준 1259번  (0) 2020.10.02
체스판 다시 칠하기 백준 1018번  (0) 2020.10.02

+ Recent posts