알고리즘

순열/조합/부분집합

E재HO 2022. 8. 28. 18:18

줄여서 순조부라고 한다.

대부분 코테를 준비할 때 처음 마주하게 되는 영역이다. 

출제비중이 그리 높진 않지만 이 개념을 모르고 구현도 못하는 상태로 코테를 보면 떨어질 것이다.  뿐만 아니라 이 개념을 알고 있으면 사고의 범위가 확장되고 문제를 풀 수 있는 카드가 하나 더 생긴다.

 

순열(Permutaition)

순서를 고려해서 나열할 수 있는 모든 경우의 수를 구하는 것이 순열이다.

순열은  n팩토리얼의 경우의 수를 가진다.

 

예를 들어

A,B,C가 있다고 가정하면 이를 나열 할 수 있는 경우는

ABC

ACB

BAC
BCA
CAB
CBA

6가지가 나온다. 즉 3! 이다.

보통 n이 11일때까지 4천만개정도의 경우로 시간 복잡도를 구할 수 있고 12부터는 4억을 초과하기때문에 다른 풀이를 고려해 봐야한다.

 

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

 

10974번: 모든 순열

N이 주어졌을 때, 1부터 N까지의 수로 이루어진 순열을 사전순으로 출력하는 프로그램을 작성하시오.

www.acmicpc.net

순열에 관한 간단한 문제이다.

package practice2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class boj10974 {
	static int N;
	static boolean[] used;
	static int[] arr;

	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(in.readLine());
		used= new boolean[N+1];
		arr= new int[N+1];
		permutation(0);
	}

	private static void permutation(int cnt) {
		if (cnt == N) {
			for (int i = 0; i < N; i++) {
				System.out.print(arr[i] + " ");
			}
			System.out.println();
			return;
		}

		for (int i = 1; i <= N; i++) {
			if (used[i])
				continue;
			used[i] = true;
			arr[cnt] = i;
			permutation(cnt + 1);
			used[i] = false;
		}
	}
}

 

조합(Combination)

n개의 원소 중에서 r개를 뽑는 경우의 수이다. 그리고조합은 순서를 고려하지 않는다. 따라서

ABC

ACB

BAC
BCA
CAB
CBA

이 모든 경우를 싸그리 묶어서 하나의 경우로 생각한다.

조합 공식은 

nCr = n!/r!(n-r)! 이다.

이걸 계산할려면 팩토리얼 계산이 오래걸리니까 직접계산은 비추천이다.

대신 느낌을 잘 파악하면 된다. 

n개중에서 r개를 뽑는 경우의 수를 구하라는 느낌이 난다면 조합으로 풀면된다.

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

 

2407번: 조합

n과 m이 주어진다. (5 ≤ n ≤ 100, 5 ≤ m ≤ 100, m ≤ n)

www.acmicpc.net

조합에 관한 문제이다.

 

 

package practice2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class boj1759 {
	static int N, M;
	static boolean[] used, alphabet;
	static char[] arr, password;

	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		alphabet = new boolean[26];
		alphabet[0] = true;
		alphabet[4] = true;
		alphabet[8] = true;
		alphabet[14] = true;
		alphabet[20] = true;
		// 모음 자리는 다 트루 표시

		String[] s = in.readLine().split(" ");
		N = Integer.parseInt(s[0]);
		M = Integer.parseInt(s[1]);
		arr = new char[M];
		password = new char[M];
		s = in.readLine().split(" ");
		for (int i = 0; i < M; i++)
			arr[i] = s[i].charAt(0);
		Arrays.sort(arr);

		comb(0, 0, 0, 0);
	}

	private static void comb(int start, int cnt, int mCnt, int jCnt) {
		if (cnt > N)
			return;
		if (cnt == N && mCnt > 0 && jCnt > 1) {
			for (int i = 0; i < N; i++)
				System.out.print(password[i]);
			System.out.println();
		} else {
			for (int i = start; i < M; i++) {
				password[cnt] = arr[i];
				if (alphabet[arr[i] - 'a'])
					comb(i + 1, cnt + 1, mCnt + 1, jCnt);
				else
					comb(i + 1, cnt + 1, mCnt, jCnt + 1);
			}
		}
	}
}

 

부분집합 (subset)

부분집합은 부분적으로 집합을 만드는 것이다.

A,B,C가 있으면

{0},{A},{B},{C},{A,B},{A,C},{B,C},{A,B,C}가 된다.

부분 집합을 구성하는 경우의 수는 2의 n승 이다.

n개가 있으면 포함시키거나/포함시키지 않거나라는 2가지 경우를 각 n개 만큼 가진다고 볼 수 있다.

 

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

 

1182번: 부분수열의 합

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

www.acmicpc.net

부분집합 문제다.

package practice2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class boj1182 {
	static int N , M, answer;
	static int arr[];
	public static void main(String[] args) throws IOException {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		String[] s = in.readLine().split(" ");
		N = Integer.parseInt(s[0]);
		M = Integer.parseInt(s[1]);
		s = in.readLine().split(" ");
		arr = new int[N];
		for(int i =0; i<s.length;i++)arr[i] = Integer.parseInt(s[i]);
		ArrayList<Integer>list = new ArrayList<>();
		subset(0,0,false);
		
		System.out.println(answer);
	}
	private static void subset(int cnt,int sum , boolean flag) {
		if(sum==M && flag ==true)answer++;
		if(cnt>=N)return;
		subset(cnt+1,sum,false);//현재 안 더한거
		subset(cnt+1,sum+arr[cnt],true);//현재 값 더한거
	}
}