Post

[Day17] 순열 / 조합 / 부분집합

[Day17] 순열 / 조합 / 부분집합

순열 (Permutation)

  • 순서가 중요하며 중복을 허용하지 않음
  • 가능한 모든 경우를 고려해야 하므로 시간복잡도는 O(n!)

(ex) 주사위 던지기 (순열)

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
import java.util.Arrays;

public class A01_주사위_일반순열 {
    static int totalCnt; // 완성된 순열의 수
    static int n; // 주사위 던지는 횟수
    static int[] results; // 순열 결과를 저장할 배열
    static boolean[] isSelected; // 중복 체크를 위한 배열

    public static void main(String[] args) {
        n = 2; // 주사위를 2번 던짐
        results = new int[n];
        isSelected = new boolean[7]; // 1~6번 index만 사용
        주사위던지기(0);
        System.out.println(totalCnt);
    }


	//주사위-일반순열(순서 o, 중복 x)
	public static void 주사위던지기(int cnt) {//cnt는 판의 횟수
		//ex. 주사위를 2번 던져서 나오는 모든 경우의 수, 한번 뽑은 걸 다시 뽑을 수 없음==> 중복 체크 코드 있음 : 30
		if(cnt==n) {// cnt는 판의 횟수
			totalCnt++;
			System.out.println(Arrays.toString(results));
			return;
		}

        for (int i = 1; i <= 6; i++) {
            if (isSelected[i]) continue;
            results[cnt] = i;
            isSelected[i] = true;
            주사위던지기(cnt + 1);
            isSelected[i] = false;
        }
    }
}

📌 Explanation

  • isSelected 배열을 사용하여 중복을 방지
  • cnt == n일 때 결과 출력
  • for문에서 사용한 숫자는 다시 사용하지 않도록 isSelected[i] = true

훈련문제 - 모든 순열 (https://www.acmicpc.net/problem/10974)

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
package 순조부;

import java.util.Scanner;
public class 백준10974_모든순열 {
	static int n;
	static int[] result;
	static boolean[] isSelected;
	static StringBuilder sb = new StringBuilder();
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		result = new int[n];
		isSelected = new boolean[n+1];	
		perm(0);
		System.out.println(sb);
	}
	
	private static void perm(int cnt) {
		if(cnt == n) {
			for(int i : result) {
				sb.append(i).append(" ");
			}
			sb.append("\n");
			return;
		}
		for(int i = 1 ; i <= n ; i++) {
			if(isSelected[i]) continue;
			result[cnt] = i;
			isSelected[i] = true;
			perm(cnt + 1);
			isSelected[i] = false;
		}
	}
}

조합 (Combination)

  • 순서는 중요하지 않으며, 중복을 허용하지 않음
  • 일반적으로 재귀를 이용하여 해결
  • 시간복잡도: O(2^n) ~ O(nCr)

(ex) 주사위 던지기 (조합)

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
import java.util.Arrays;

public class A02_주사위_일반조합 {
	static int totalCnt; // 완성된 조합의 수
	static int n;// 주사위 던지는 횟수
	static int[] results;// 조합 결과를 저장할 배열
	
	public static void main(String[] args) {
		n = 2; // 2회
		results = new int[n];
		//첫 번째 인수는 판 번호로 0부터 시작해야 배열에 집어 넣기 좋다.
		주사위던지기(0, 1);//두 번째 인수는 순서와 중복을 없애기 위한 아이디어.최초에는 주사위의 가장 작은 숫자 1부터 부여.
		System.out.println(totalCnt);
	}

	// 주사위-일반조합
	public static void 주사위던지기(int cnt, int start) {// 일반조합(순서 x,중복 x) 
		if (cnt == n) {
			totalCnt++; //최종 15
			System.out.println(Arrays.toString(results));
			return;
		}
		for (int i = start; i <= 6; i++) {
			results[cnt] = i;
			주사위던지기(cnt + 1, i + 1); //두 번째 판을 던지러 갈 때 지금의 i 값보다 1큰수로 시작하게 하면 중복과 순서를 모두 피할 수 있다
		}
	}
}

📌 Explanation

  • start 값을 사용하여 중복을 방지
  • 주사위던지기(cnt + 1, i + 1); → 이전 값보다 큰 숫자만 선택

부분집합 (Subset)

  • 모든 경우의 조합을 고려해야 하며, 선택하거나 선택하지 않는 2가지 경우로 진행
  • 시간복잡도: O(2^n)

(ex) 주사위 숫자의 부분집합

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
package test;

public class A03_주사위_부분집합 {	
	static int totalCnt; // 부분집합의 총 개수를 저장하는 변수	
	static int[] input={1, 2, 3, 4, 5, 6};// 주사위의 숫자를 가진 배열	
	static boolean[] isSelected;// 각 숫자가 선택되었는지를 저장하는 배열
	
	public static void main(String[] args) {		
		
		isSelected = new boolean[6];	// 선택 여부를 저장할 배열 초기화		
		subset(0);// 부분집합 생성 함수 호출		
		
		System.out.println(totalCnt);// 생성된 부분집합의 총 개수 출력
	}
	
	// 부분집합 생성 함수
	public static void subset(int cnt) {
		// cnt가 6이면 모든 원소를 고려한 상태이므로 부분집합을 하나 완성한 것
		if (cnt == 6) {
			// 완성된 하나의 부분집합 개수 증가
			++totalCnt;
			// 현재 완성된 부분집합 출력
			for (int i = 0; i < 6; i++) {
				// 선택된 원소는 출력하고, 선택되지 않은 원소는 "X"로 표시
				System.out.print((isSelected[i] ? input[i] : "X") + " ");
			}
			System.out.println(); 
			return; 
		}
		
		// 현재 원소를 선택하는 경우
		isSelected[cnt] = true; // 선택 상태로 설정
		subset(cnt + 1); // 다음 원소로 진행
		
		// 현재 원소를 비선택하는 경우
		isSelected[cnt] = false; // 비선택 상태로 설정
		subset(cnt + 1); // 다음 원소로 진행
	}
}

📌 Explanation

  • isSelected[cnt] = true → 현재 원소 포함
  • isSelected[cnt] = false → 현재 원소 미포함
  • 모든 경우를 탐색

✅ 정리

구분중복순서시간복잡도
순열XOO(n!)
조합XXO(nCr)
부분집합-XO(2^n)

🔹 언제 사용할까?

  • 순열: 모든 경우의 수를 고려할 때 (ex. 암호 생성, 경로 탐색)
  • 조합: 선택된 원소들만 고려할 때 (ex. 팀 구성, 메뉴 조합)
  • 부분집합: 특정 조건을 만족하는 부분 집합을 찾을 때 (ex. 특정 합을 만드는 집합)

👉🏻내가 만든 예제


👉🏻팀플 알고리즘

This post is licensed under CC BY 4.0 by the author.