[코딩테스트] 브루트 포스(순열)

reference : 2021 코딩테스트 기초(최백준) 강의를 공부하며 정리한 내용입니다.

  • 순열 : 임의의 수열을 다른 순서로 섞는 연산
  • A = [1,5,6]인 경우[1,5,6], [1,6,5], [5,1,6], [5,6,1], [6,1,5], [6,5,1]
  • 크기 N인 수열의 순열 N!개
  • 브루투포스 문제인데 순서가 중요한 경우(N가지의 순서 바꾸는 것만 가능할때)
    • 첫 순열을 만든다. : 오름차순(비내림차순 정렬)
    • 위를 이용해 다음 순열을 구한다. (반복)
    • 마지막 순열 : 내림차순(비오름차순 정렬)

다음 순열 (10972번)

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

  • 다음 순열은 C++일 경우 next_permutation, prev_permutation 이용, 자바는 직접 구현, 파이썬은 itertools
  • 이 순열이 무엇으로 시작하는 마지막 순열인지 알아보고 첫순열 만들어주기

    1324
    1342 : 13으로 시작하는 마지막 순열
    1423 : 14로 시작하는 첫 순열

  1. A[i-1] < A[i]를 만족하는 가장 큰 i를 찾는다 O(N)
    마지막순열이 가장 긴 걸 찾아주기 (ex. A=7236541 은 723으로 시작하는 마지막 순열) : 어디까지 내림차순인지 확인하기
  2. j≥i 이면서 A[j] > A[i-1]를 만족하는 가장 큰 j를 찾는다 O(N)
    마지막순열은 내림차순이니 j는 가장 오른쪽의 수이기 때문에 가장 큰 j라는 표현을 사용함, 7236541 이면 3보다 크고 가장 작은 수(4)를 찾기
  3. A[i-1]과 A[j]를 swap 한다 O(1)
    ex) 7236541 -> 7246531
  4. A[i]부터 순열을 뒤집는다 O(N)
    ex) 7246531 -> 7241356 완성!
  • 각각 시간복잡도를 더해주면 다음순열을 구하는 데 걸리는 시간 O(N), 모든 순열은 O(N x N!)
  • 함수에 주어진 순열을 변경할거고 리턴값 true : 다음순열 있음 / false : 다음 순열 없음
  • c++ 예

    bool next_permutation(int *a, int n){
    	//1단계
    	int i = n-1;
    	while(n>0 && a[i-1] >= a[i]) i-=1; // 1단계
    	if(i<=0) return false; //마지막순열
    	//2단계
    	int j = n-1;
    	while(a[j] <= a[i-1]) j-=1;
    	//3단계
    	swap(a[i-1], a[j]);
    	//4단계
    	j = n-1;
    	while(i<j){
    		swap(a[i], a[j]);
    		i += 1;
    		j -= 1;
    	}
    	return true;
    }
    
  • java 코드

    import java.util.*;
    
    public class Main {
    	public static void main(String[] args) {
    		Scanner sc = new Scanner(System.in);
    		int n = sc.nextInt();
    		int[] a = new int[n];
    		for(int i=0; i<n; i++) {
    			a[i] = sc.nextInt();
    		}
    		if(next_permutation(a)) {
    			for(int i=0; i<n; i++) {
    				System.out.print(a[i] + " ");
    			}
    			System.out.println();
    		} else {
    			System.out.println("-1");
    		}
    	}
    	public static boolean next_permutation(int[] a) {
    		//1단계
    		int i = a.length-1;
    		while(i>0 && a[i-1] >= a[i])
    			i-=1;
    		if(i<=0)
    			return false;
    		//2단계
    		int j = a.length-1;
    		while(a[j]<=a[i-1])
    			j-=1;
    		//3단계
    		int temp = a[i-1];
    		a[i-1] = a[j];
    		a[j] = temp;
    		//4단계
    		j = a.length-1;
    		while(i<j) {
    			temp = a[i];
    			a[i] = a[j];
    			a[j] = temp;
    			i += 1;
    			j -= 1;
    		}
    		return true;
    	}
    }
    

이전순열(10973번)

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

풀이

  • 다음 순열을 하는 것들을 모두 반대로
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] a = new int[n];
		for(int i=0; i<n; i++) {
			a[i] = sc.nextInt();
		}
		if(prev_permutation(a)) {
			for(int i=0; i<n; i++) {
				System.out.print(a[i] + " ");
			}
			System.out.println();
		} else {
			System.out.println("-1");
		}
	}
	public static boolean prev_permutation(int[] a) {
		//1단계
		int i = a.length-1;
		while(i>0 && a[i-1] <= a[i])
			i-=1;
		if(i<=0)
			return false;
		//2단계
		int j = a.length-1;
		while(a[j]>=a[i-1])
			j-=1;
		//3단계
		int temp = a[i-1];
		a[i-1] = a[j];
		a[j] = temp;
		//4단계
		j = a.length-1;
		while(i<j) {
			temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			i += 1;
			j -= 1;
		}
		return true;
	}
}

모든순열(10974번)

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

풀이

  • 다음 순열을 구하는 함수가 false 리턴할 때까지 계속 반복
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] a = new int[n];
		for(int i=0; i<n; i++) {
			a[i] = i+1;
		}
		for(int i=0; i<n; i++) {
			System.out.print(a[i] + " ");
		}
		System.out.println();
		while(next_permutation(a)) {
			for(int i=0; i<n; i++) {
				System.out.print(a[i] + " ");
			}
			System.out.println();
		}
	}
	public static boolean next_permutation(int[] a) {
		int i = a.length-1;
		while(i>0 && a[i-1] >= a[i])
			i-=1;
		if(i<=0)
			return false;

		int j = a.length-1;
		while(a[j]<=a[i-1])
			j-=1;

		int temp = a[i-1];
		a[i-1] = a[j];
		a[j] = temp;

		j = a.length-1;
		while(i<j) {
			temp = a[i];
			a[i] = a[j];
			a[j] = temp;
			i += 1;
			j -= 1;
		}
		return true;
	}
}
  • 이렇게도 통과되긴 하는데 do while 쓰는게 더 좋은 거 같다!!
  • 10! = 3900만이라 safe 인데 11! 만 가도 4억이 넘음, 그래서 순서 이용하는 브루투포스는 보통 N≤10 이다.

댓글남기기