조합의 모든것 풀어봅시다
본문 바로가기
알고리즘 모음집/알고리즘 (Algorithm)

조합의 모든것 풀어봅시다

by KyeongMin 2019. 8. 20.
728x90
반응형

조합은 무엇일까요? 재귀를 이용하여 조합을 뽑기 전에 조합은 어떤 모양을 가지고 있는지

알아야 함으로 어떻게 생겼는지 먼저 알아봅시다.

 조합은  Combination 으로 nCr n개의 나열된 숫자에서 r개를 뽑는 것을 말합니다.

하지만 앞서있는 블로그에 순열의 모든것 풀어봅시다 에서 순열과 다른 점은

순서가 달라도 내용물이 같다면 같은 집합임으로 그 경우를 빼고 뽑아야 합니다. 그래서 

순열보다 반 정도가 적게 나오게 됩니다.

예를 들어 1, 2, 3, 4의 숫자에서 2개의 숫자를 뽑는다면 경우는 

노란 블록인 경우와 파란 블록 중 하나인 경우가 조합입니다. 그냥 쉽게 노란 블록이라고 보시면 되고 

파란 블록은 노란 블록가 위치만 다를 뿐 같은 내용물을 가지고 있으므로 같은 경우이기 때문에 제외하셔야 합니다.

문제를 보시면 더 이해가 될 것입니다. 문제를 봐보겠습니다.

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

불러오는 중입니다...

 

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
#include<stdio.h>
int N, M;
const int DSIZE = 8;
 
int D[DSIZE];
 
void init() {
    scanf("%d %d"&N, &M);
}
void dfs(int idx, int d){
    if (idx == M) {
        for (int i = 0; i < M; i++)printf("%d ", D[i]);
        printf("\n");
        return;
    }
    for (int i = d; i <= N; i++) {
 
            D[idx] = i;
            dfs(idx + 1, i + 1);
            D[idx] = 0;
    }
}
int main(void) {
    init();
    dfs(0,1);
    return 0;
}
 
 

처음 소스를 보시면 응? 순열이랑 같은데 뭐가 달라 이렇게 생각하시는 분이 있습니다.

하지만 우선 dfs가 갖는 매개변수가 두 개이고 dfs안에 있는 포문이 1부터 시작이 아닌

d부터 시작합니다. 그렇게 하는 이유는 그래야 같은 경우를 거를 수 있는데 

조합은 뭐다? 이전 수의 참조를 하면 안 된다 생각해야 합니다. 무슨 소리지 하는 분들을 위해

간단히 말씀드리면 

뽑아야 한다는 소리입니다. 그렇게 뽑기 위해서 

조합 dfs

 위와 같이 하시면 되는데 이해가 나는 안 간다 왜 저렇게 해야 하지 하면 솔직히 디버깅을 통해서 어떤 식으로 배열에 

들어가는지 확인하시면 되는데 이것도 시간낭비고 난 시간이 없다 하면 그냥 외우는 것을 추천드립니다.

결국은 외우셔도 이해를 하게 되는 건 같다고 생각합니다. 대신 꾸준히 봐주세요!!!

 

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

 

15655번: N과 M (6)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열 고른 수열은 오름차순이어야 한다.

www.acmicpc.net

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
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
int N, M;
const int DSIZE = 8;
const int inputSIZE = 8;
int D[DSIZE];
int input[inputSIZE];
void init() {
    scanf("%d %d"&N, &M);
    for (int i = 0; i < N; i++) {
        scanf("%d"&input[i]);
    }
    sort(input, input + N);
}
void dfs(int idx, int d) {
    if (idx == M) {
        for (int i = 0; i < M; i++)printf("%d ", D[i]);
        printf("\n");
        return;
    }
    for (int i = d; i < N; i++) {
        D[idx] = input[i];
        dfs(idx+1,i + 1);
        D[idx] = 0;
    }
}
int main(void) {
    init();
    dfs(00);
    return 0;
}
 
 

조합에 대해서 기본 틀을 익혔다면 나머지 문제는 순열 문제랑 비슷하기 때문에

쉽게 푸실 수 있습니다. 나는 자신감이 생겨 나머지는 해답 안 보고 해야지 하시는 분은

지금부터 문제를 푸시고 풀면 좋고 못 풀면 좌절하지 마시고 블로그에 방문하셔서

확인하고 이렇게 하구나 하고 다음날 다시 풀어보시면 됩니다. 알고리즘은 답을 보면 이거다 하는데

다음날 다시 풀면 못 풀 수도 있어서 잊혔다 싶을 때 반복하다 보면 당연히 이렇게 해야지 라고 할 수 있습니다.

갑자기 말이 길어졌는데  다음 문제는 

입력받은 숫자를 이용하여 조합을 구하는 방법으로 앞서 설명한 것과 마찬가지고 

D에 배열에  i를 넣어던것을  입력받은 것을 저장한 input [i]를 대입만 해주면 되는 것입니다.

정말 간단하지 않나요?

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

 

15664번: N과 M (10)

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

www.acmicpc.net

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
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
const int DSIZE = 8;
const int inputSIZE = 8;
int D[DSIZE]; int input[inputSIZE];
int N, M;
void init() {
    scanf("%d %d"&N, &M);
    for (int i = 0; i < N; i++) {
        scanf("%d"&input[i]);
    }
    sort(input, input + N);
}
void dfs(int idx, int d) {
    if (idx == M) {
        for (int i = 0; i < M; i++printf("%d ", D[i]);
        printf("\n");
        return;
    }
    int before = 0;
    for (int i = d; i < N; i++) {
        if (before != input[i]) {
            before = input[i];
            D[idx] = input[i];
            dfs(idx + 1, i + 1);
            D[idx] = 0;
        }
    }
}
int main(void) {
    init();
    dfs(00);
    return 0;
}
 
 
조합의 그자체 아닙니까? 그냥 단순히 중복인경우만 거를수 있도록
before변수를 두어 체크만 해주시고 가능한 조건이면 넣기만 하면됩니다.

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

 

15652번: N과 M (4)

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

www.acmicpc.net

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
#include<stdio.h>
#include<algorithm>
using namespace std;
const int DSIZE = 8;
int D[DSIZE];
int N, M;
void init() {
    scanf("%d %d"&N, &M);
}
void dfs(int idx) {
    if (idx == M) {
        for (int i = 0; i < M; i++)printf("%d ", D[i]);
        printf("\n");
    }
    for (int i = 1; i <= N; i++) {
        if (idx < M&&(idx==0||D[idx-1]<=i)) {
            D[idx] = i;
            dfs(idx + 1);
            D[idx] = 0;
        }
    }
}
int main(void) {
    init();
    dfs(0);
    return 0;
}
 
 

조합이 아니고 순열 아니야 라고 생각하실 수 있지만 흠 어떤 면에서 보면 같은 것인데 

조합에 더 가깝다 생각 드는 건 같은 집합인 경우가 없기 때문에 조합에 더 가깝다 생각합니다.

무튼 여기서는 

1 2 3 4 

이면 자신과 같은 경우는 집합으로 사용해야 하므로 조건을

이렇게 주시면 되는데 의미는 idx==0인 경우에 D [idx-1]은 오류가 날 수 있기도 하고 

0인 경우는 if조건에 들어가야 하므로 저렇게 조건을 주고 

D [idx-1] 즉 여기서 는 순서대로 숫자가 커지는 식으로 자기 자신과 같은 수이 상인 것을

집합으로 가지고 있어야 하므로 저렇게 조건을 주시면 됩니다.

모르겠다 하면 외우시면 됩니다. 파이팅~!!!

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

 

15657번: N과 M (8)

N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다. N개의 자연수 중에서 M개를 고른 수열 같은 수를 여러 번 골라도 된다. 고른 수열은 비내림차순이어야 한다. 길이가 K인 수열 A가 A1 ≤ A2 ≤ ... ≤ AK-1 ≤ AK를 만족하면, 비내림차순이라고 한다.

www.acmicpc.net

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
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
const int DSIZE = 8;
const int inputSIZE = 8;
int D[DSIZE];
int input[inputSIZE];
int N, M;
void init() {
    scanf("%d %d"&N, &M);
    for (int i = 0; i < N; i++) {
        scanf("%d"&input[i]);
    }
    sort(input, input + N);
}
void dfs(int idx) {
    if (idx == M) {
        for (int i = 0; i < M; i++)printf("%d ", D[i]);
        printf("\n");
        return;
    }
    for (int i = 0; i < N; i++) {
        if (idx < M&&(idx==0||D[idx-1]<=input[i])) {
            D[idx] = input[i];
            dfs(idx + 1);
            D[idx] = 0;
        }
    }
}
int main(void) {
    init();
    dfs(0);
    return 0;
}
 
 

지금 이문제는 위에 문제의 확장판 입력된 값에 대해서  자기와 같은 수이 상의 집합을

만들면 됩니다. 위에 소스에서 i를 input [i]로 한 차이입니다.

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

 

15666번: N과 M (12)

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

www.acmicpc.net

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
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
const int DSIZE = 8;
const int inputSIZE = 8;
int D[DSIZE];
int input[inputSIZE];
int N, M;
void init() {
    scanf("%d %d"&N, &M);
    for (int i = 0; i < N; i++) {
        scanf("%d"&input[i]);
    }
    sort(input, input + N);
}
void dfs(int idx) {
    if (idx == M) {
        for (int i = 0; i < M; i++)printf("%d ", D[i]);
        printf("\n");
        return;
    }
int    before = 0;
    for (int i = 0; i < N; i++) {
        if (idx < M&&(idx==0||D[idx-1]<=input[i])&&before!=input[i]) {
            before = input[i];
            D[idx] = input[i];
            dfs(idx + 1);
            D[idx] = 0;
        }
    }
}
int main(void) {
    init();
    dfs(0);
    return 0;
}
 
 

조합의 마지막 문제!!!!!!

입력값에 같은 숫자 있는 경우 저렇게 같은 수이 상의 집합을 뽑는 소스에 

이전의 값을 before 변수에 넣고 같지 않을 때만 넣어주면 됩니다.

정말 쉽지 않나요? 저렇게 조건에 맞추어 생각하시고 조건을 주시면 간단합니다.

처음에는 물론 이해가 안 될 수 있지만 하다 보면 되고 ,

이런 말이 있지 않습니까? 어려운 일은 있을 수 있어도 하지 못하는 일은 없다는 말이 있습니다.

어려울 수 있습니다. 하지만 하지 못하는 건 아닙니다. 할 수 있습니다. 

오늘은 여기까지이고 긴 글 봐주셔서 감사합니다.

 

 

728x90
반응형

'알고리즘 모음집 > 알고리즘 (Algorithm)' 카테고리의 다른 글

백준 11559 puyo puyo  (0) 2019.09.08
백준 16197 두 동전  (0) 2019.09.08
순열의 모든것 풀어봅시다  (0) 2019.08.20
백준 1244 스위치 켜고 끄기  (0) 2019.07.28
백준 1941 소문난 칠공주  (0) 2019.07.28

댓글