백준 10972 - 다음 순열

Updated:

Java

10972 번 - 다음 순열

문제

1부터 N까지의 수로 이루어진 순열이 있다. 이때, 사전순으로 다음에 오는 순열을 구하는 프로그램을 작성하시오.

사전 순으로 가장 앞서는 순열은 오름차순으로 이루어진 순열이고, 가장 마지막에 오는 순열은 내림차순으로 이루어진 순열이다.

N = 3인 경우에 사전순으로 순열을 나열하면 다음과 같다.

  • 1, 2, 3
  • 1, 3, 2
  • 2, 1, 3
  • 2, 3, 1
  • 3, 1, 2
  • 3, 2, 1

접근 방법

처음에는 DFS로 해결하려 하였지만, n이 최대 10000이므로 최악의 경우 모든 순열을 계산하면 10000!까지 생각해야 하므로 시간 초과가 날 수 밖에 없는 문제였다.
따라서 문제의 규칙을 보고 해결하였다.
N = 3인 경우보다 N이 4인 경우가 더 규칙을 찾기 쉬우므로, 이를 기준으로 설명하면

N=4 인 모든 순열 : 
1 2 3 4
1 2 4 3
1 3 2 4
1 3 4 2
1 4 2 3
1 4 3 2
2 1 3 4
2 1 4 3
2 3 1 4 
2 3 4 1
2 4 1 3
2 4 3 1
3 1 2 4
3 1 4 2
3 2 1 4
3 2 4 1
3 4 1 2
3 4 2 1
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
  1. 오른쪽에서 왼쪽으로 이동하면서 n과 n-1을 비교한다.
  2. n-1 < n인 경우를 찾는다. 찾은 인덱스를 기준으로 왼쪽 영역과 오른쪽 영역을 나눌 수 있다.
  3. 해당 인덱스에 있는 숫자를 기준으로 삼는다.
  4. 오른쪽 영역에서 오른쪽부터 왼쪽영역으로 움직이면서 기준 값과 크기를 비교한다.
  5. 크다면 자리를 바꾼다.
  6. 오른쪽영역의 숫자를 오름차순으로 정렬해준다.
  7. 만약 4 3 2 1과 같이 마지막 순열이면 위 2~6번 과정을 거치지 않으므로, 이를 확인한다.

코드

/*
2504번 - 괄호의 값
https://www.acmicpc.net/problem/2504
*/

import java.util.*;
import java.io.*;

public class Main {
	static int n;
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	// 초기화
    	n = stoi(br.readLine()); 
    	int[] perm = new int[n];
    	
    	StringTokenizer stk = new StringTokenizer(br.readLine());
    	for(int i = 0; i < n; i++) {
    		perm[i] = stoi(stk.nextToken());
    	}
    	boolean isFinded = false;
    	// 1번 : 오른쪽 끝에서 부터 탐색
    	end: for(int i = n - 1; i >= 1; i--) {
			// 2번 : i-1 < i인 경우
    		if(perm[i] > perm[i-1]) {
    			int strd = i - 1;	// 3번 : 해당 값을 기준(standard)으로 정한다
    			for(int j = n - 1; j >= i; j--) {
				// 4번 : 오른쪽부터 왼쪽영역으로 움직이면서 기준 값과 크기를 비교
    				if(perm[strd] < perm[j]) {
    					// 5번 : 크다면 자리를 바꾼다.
    					int temp = perm[strd];
    					perm[strd] = perm[j];
    					perm[j] = temp;
    					// 6번 : 오른쪽영역의 숫자를 오름차순으로 정렬해준다.
    					int[] forsort = new int[n-i];
    					for(int k = 0; k < n-i; k++)
    						forsort[k] = perm[i+k];
    					Arrays.sort(forsort);
    					for(int k = i; k < n; k++)
    						perm[k] = forsort[k - i];
    					// 7번 : 다음 순열을 찾았으므로, 알려준다.
    					isFinded = true;
    					break end;
    				}
    			}
    		}
    	}
	// 순열의 마지막 끝인 경우
    	if(!isFinded) {
    		System.out.println(-1);
			br.close();
    		return;
    	}
    	
    	for(int v : perm)
    		System.out.print(v + " ");
    	br.close();
	}

	static int stoi(String str) {
    	return Integer.parseInt(str);
    }
}
DFS로 실패한 코드
import java.util.*;
import java.io.*;

public class Main {
	static int n;
	static boolean[] vis;
	static int[] sel;
	static int[] comp;
	static int cnt = 0;
	static ArrayList<Integer> result = new ArrayList<Integer>();
	static boolean isSame = true;
	static boolean getResult = false;
	public static void main(String[] args) throws IOException {
		//System.setIn(new FileInputStream("res/mainInput.txt"));	//제출 할 때 주석해야함
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    	// 초기화
    	n = stoi(br.readLine()); 
    	vis = new boolean[n + 1];
    	sel = new int[n];
    	comp = new int[n];
    	
    	StringTokenizer stk = new StringTokenizer(br.readLine());
    	for(int i = 0; i < n; i++) {
    		comp[i] = stoi(stk.nextToken());
    	}
    	
    	// 순열 만들기
    	DFS(0);
    	
    	if(result.size() == 0) {
    		System.out.println(-1);
    		br.close();
    		return;
    	}
    	for(int i = 0; i < n; i++) {
    		System.out.print(result.get(i) + " ");
    	}
    	System.out.println(cnt);
    	br.close();
	}
	
	static boolean DFS(int lv) {
		if(lv == n) {
			cnt++;
			if(getResult) {		// 다음 순열을 저장한다.
				for(int i = 0; i < n; i++) {
					result.add(sel[i]);
				}
				return true;			// 다음 순열을 찾았으므로 DFS 종료
			}
			isSame = true;
			for(int j = n - 1; j >= 0; j--) {
    			if(sel[j] != comp[j]) {		// 주어진 순열 값과 같은지 바교
    				isSame = false;
    				break;
    			}
    		}
			if(isSame) {		// N개의 값이 같으면 다음 순열을 출력하도록 변수 값을 준다.
				getResult = true;
			}
			return false;
		}
		for(int i = 1; i <= n; i++) {
			if(!vis[i]) {
				vis[i] = true;
				sel[lv] = i;
				if(DFS(lv + 1)) {
					return true;
				}
				vis[i] = false;
			}
		}
		return false;
	}
	
	static int stoi(String str) {
    	return Integer.parseInt(str);
    }
}

총평

난이도

★★★★★

후기

dfs 문제 일거 같았지만 완전히 속은 문제.
스터디를 통해 혹시 DFS로 해결하신 분이 있으신지 여쭤봐야겠다.

개선할 점

스스로 해결하지 못 푼 문제, 수학 / DP는 창의력을 떠올리기 너무 힘들다.