코딩 테스트 준비! (백준)/GREEDY

[Greedy] 백준 10972번 다음 순열 (C++)

lee-soo 2025. 4. 8. 17:41

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

 

해당 문제는 순열의 길이를 입력하고 (n) , 1과n으로 이루어진 특정 순열을 입력 시, 다음 순열을 출력하는 프로그램이다.

 

 

 

 

 

--다음은 틀린 풀이..--

뭐 내가 앞서, n과m이라는 순열 문제를 풀어보았으니, 그렇게 한번 풀어볼까?

 

(참고)

https://lee-soo.tistory.com/17

 

[DFS] 백준 15649번 N과 M (1,2,3) (C++)

https://www.acmicpc.net/problem/15649 이 문제를 풀면서 나에게도 큰 시련이 찾아왔다. 도저히 못풀겠다 사실 글쓴이는 아직 코테 초보수준이라 해본게 브루트 포스랑, dp 랑.. 자료구조 뭐 이런것들인

lee-soo.tistory.com

 

그냥, 모든 경우의 수를 백트래킹을 통해서 본 후에 그 다음꺼 출력하면 되는거 아니야?

 

만약에 마지막 경우라면 -1을 출력하고

 

#include <iostream>
#include <vector>
#include <algorithm>
bool visited[10];
using namespace std;
void task(int n, int depth, int *arr);
vector<int> result;
bool sig = 0;
int main()
{
    int n;
    cin >> n;
    int arr[n];
    for (int i = 0; i < n; i++)
        cin >> arr[i];

    task(n, 0, arr);
}
void task(int n, int depth, int *arr)
{
    if (sig == 2)
    {
        cout << "-1\n";

        return;
    }

    if (sig == 1 && n == depth)
    {
        for (int i = 0; i < n; i++)
        {
            cout << result[i] << " ";
        }
        cout << "\n";
        sig = 0;
        return;
    }
 
    if (sig != 1 && n == depth)
    {
        for (int i = 0; i < n; i++)
        {
            if (result[i] != arr[i])
            {
                return;
            }
        }
        sig = 1;
        return;
    }

    for (int i = arr[0]; i <= n; i++)
    {
        if (!visited[i])
        {
            visited[i] = true;
            result.push_back(i);
            task(n, depth + 1, arr);
            result.pop_back();
            visited[i] = false;
        }
    }
}
이 코드는 -1은 구현하지 않고 우선 다음 순열만 출력하는 경우를 고려한 코드인데
 
시간초과가 걸린다..
 
생각해보니 앞선 N과 M문제는 8이하의 정수였는데 여기는 10000이하의 정수가 나오니 당연할 따름이지
 
그렇다면 다른 방식으로 접근을 해야한다!!!
 
------
 
순열에 대해 대략적인 파악이 중요하다
 
사실 이 규칙을 찾는게 처음엔 어려울 수 있다.
 
예를들어 우리가 1 3 2 5 4 라는 숫자가 있고 이 다음 순열을 구하기 위해선 어떻게 해야할까?
 
우선 이 다음 순열부터 말하자면 1 3 4 2 5 이다.
 
왜냐면
 

 

뒤에있는 5 4를 크기가 2인 부분 순열로 보자면 이미 순열의 끝부분이다

그렇다면 이제 크기를 3으로 늘려서 2 5 4에 대한 다음 순열을 봐야 한다

이때 

 

2보다 다음으로 큰 수가 맨 앞에온 후, 나머지 숫자에 대해 정렬을 해주면 된다!

 

EX)

 

1 3 2 5 4 ->

1 3 4 5 2->

"5 2" 오름차순 정렬

 

1 3 4 2 5 

정답은 이것이다.

 

즉 

오름차순이 끝나는 수 앞 수를 n이라고 할때 이 n과 , 그 이후의 수 중 n보다 다음으로 큰 수를 교환하고

 

그 이후 정렬을 하면 된다는 것!

 

이를 코드로 옮겨보자

 

 

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
    int n;
    int time;
    cin >> n;
    int arr[n];
    for (int i = 0; i < n; i++)
        cin >> arr[i];

    for (int i = n - 1; i >= 0; i--)
    {
        if (arr[i] > arr[i - 1])
        {
            time = i - 1;
            break;
        }
    }
    int min_max = time + 1;

    for (int i = time + 1; i < n; i++)
    {
        if (arr[time] < arr[i] && arr[min_max] > arr[i])
            min_max = i;
    }
    if (time == -1)
    {
        cout << "-1\n";
        return 0;
    }
    int temp;
    temp = arr[min_max];
    arr[min_max] = arr[time];
    arr[time] = temp;

    sort(arr + time + 1, arr + n);
    for (int i = 0; i < n; i++)
        cout << arr[i] << " ";
    cout << "\n";
}

 

 for (int i = n - 1; i >= 0; i--)
    {
        if (arr[i] > arr[i - 1])
        {
            time = i - 1;
            break;
        }
    }
에서 time은 오름차순이 끝나는 순간의 인덱스를 얻으며
for (int i = time + 1; i < n; i++)
    {
        if (arr[time] < arr[i] && arr[min_max] > arr[i])
            min_max = i;
    }
해당 조건문을 통해, 비교하려는 수보단 크고, 결정된 수 중에서 작은수가 있으면 그 작은수를 다시 min_max로 옮겨준다

 

 if (time == -1)
    {
        cout << "-1\n";
        return 0;
    }
 
이때 마지막 수라면, 아마 오름차순의 끝이 -1이 될 것이다(인덱스가 i-1인데, i가 0일때까지 갔으니)
그래서 이떄는 -1을 출력하고
 
이후 사건에 대해서는 바꾼 후 sort를 통해서 답을 출력!

 

algorithm 라이브러리에 포함된 함수중 permutation이라고 다음 순열을 찾아주는 함수가 있다고 하는데, 그걸 사용하면 굉장히 쉬울테니~ 다들 각자의 방법으로 한번씩 풀어보자.

이와 유사한 문제로

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

(이전순열)


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

(모든순열) 

n과m 시리즈와 굉장히 유사한 문제

 

문제를 푸는 방식은 대동소이하니 굳이 설명하진 않겠다~