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


백준저지 14889번 스타트와 링크 문제 풀이이다.


N값이 크지 않으므로 dfs를 통한 완전 탐색으로 문제 해결이 가능하다.


다음은 풀이 코드이다.



 

#include <bits/stdc++.h>

using namespace std;

bool team[20];
int n;
int s[20][20];
int ans;
int abs(int v) {
    if (v<0) return -v;
    else return v;
}
void check() {
    int start=0;
    int link=0;
    vector<int> ss, ll;
    ss.reserve(n/2);
    ll.reserve(n/2);
    for (int i=0; i <n; i++) {
        if (team[i]) {
            ss.push_back(i);
        } else {
            ll.push_back(i);
        }
    }
    for (int i=0; i < n/2; i++) {
        for (int j=0; j < n/2; j++) {
            if (i==j) continue;
            start += s[ss[i]][ss[j]];
        }
    }
    for (int i=0; i < n/2; i++) {
        for (int j=0; j < n/2; j++) {
            if (i==j) continue;
            link += s[ll[i]][ll[j]];
        }
    }
    ans = min(ans, abs(start-link));
}
void dump() {
    for (int i=0; i < n; i++) {
        cout << team[i] << ", ";
    }
    cout << '\n';
}
void dfs(int i, int j) {
    if (j > n-i) return;
    if (i >= n) {
        if (j == 0) {
            // dump();    
            check();
        }
        return;
    }
    team[i] = true;
    dfs(i+1, j-1);
    team[i] = false;
    dfs(i+1, j);
}
int main() {
    ans = 987654321;
    cin >> n;
    for (int i=0; i < n; i++) {
        for (int j=0; j < n;j++) {
            cin >> s[i][j];
        }
    }
    dfs(0, n/2);
    cout << ans << '\n';
    return 0;
}


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


백준 14888번 문제 연산자 끼워넣기 풀이이다.


N값이 크지 않으므로 dfs를 통한 완전탐색으로 문제를 해결할 수 있다.



 

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll, ll> pll;
int arr[12];
int n;

//returns (min, max) value
pll dfs(ll lval, int index, int plus, int minus, int mult, int div) {
    if (index >= n) return make_pair(lval, lval);
    ll rmin = 9876543210000LL;
    ll rmax = -9876543210000LL;
    
    ll rval = (ll)arr[index];
    if (plus > 0) {
        pll ret = dfs(lval + rval, index+1, plus-1, minus, mult, div);
        rmin = min(rmin, ret.first);
        rmax = max(rmax, ret.second);
    }
    if (minus > 0) {
        pll ret = dfs(lval - rval, index+1, plus, minus-1, mult, div);
        rmin = min(rmin, ret.first);
        rmax = max(rmax, ret.second);
    }
    if (mult > 0) {
        pll ret = dfs(lval * rval, index+1, plus, minus, mult-1, div);
        rmin = min(rmin, ret.first);
        rmax = max(rmax, ret.second);
    }
    if (div > 0) {
        pll ret = dfs(lval / rval, index+1, plus, minus, mult, div-1);
        rmin = min(rmin, ret.first);
        rmax = max(rmax, ret.second);
    }
    
    return make_pair(rmin, rmax);
}
int main() {
    
    cin >> n;
    for (int i=0; i <n; i++) {
        cin >> arr[i];
    }
    int a,b,c,d;
    cin >> a >> b >> c >> d;
    pll ans = dfs(arr[0], 1, a, b, c, d);
    cout << ans.second << '\n' << ans.first << endl;
	return 0;
}




누적합의 필요성

알고리즘 문제를 풀다보면 배열의 부분합을 빠르게 구해야 하는 경우가 있다. 

필요할 때 마다 구하게 되면 구할 때 마다 //(O(N)//)의 시간 복잡도를 갖는다.


종만북 1번 문제 페스티벌을 한번 보자.

https://algospot.com/judge/problem/read/FESTIVAL


가장 Naive한 알고리즘의 경우, 연속된 구간합을 구해서 그때그때마다 최소인지 모두 확인해 보는 방법인데, 구간합을 구하면 //(O(N^2)//)의 시간 복잡도를 갖기 때문에 상당히 느리다.

하지만 누적합을 이용하면 연속된 구간합을 //(O(N)//)만에 구할 수 있다.


누적합의 개념

대충 아래와 같은 수열이 있다고 해 보자. 총 5개의 원소를 가지고 있다.

인덱스 번호

//(1//)

//(2//)

//(3//)

//(4//)

//(5//)

//(a\scriptstyle i//) 

//(3//)

//(5//)

//(7//)

//(1//)

//(4//)

//(S\scriptstyle i//) 

//(3//)

//(8//)

//(15//)

//(16//)

//(20//)


위 수열에서 연속된 구간의 합, 예를들어 //(a\scriptstyle 2//)부터 //(a\scriptstyle 4//)까지의 합을 구하려고 한다면, 3개의 원소 값을 참조해야 한다.

이러한 점을 보완하기 위해서 누적합 수열 //(S\scriptstyle i//)를 도입해보자. 

//(S\scriptstyle i//)는 //(a\scriptstyle 1//)부터 //(a\scriptstyle i//)까지의 모든 원소의 합을 자신의 값으로 갖는다. 또한 //(S\scriptstyle 0\textstyle=0//)이다.

//(i//)번째 원소부터 //(j//)번째 원소의 구간합은 //(S\scriptstyle j\textstyle - S\scriptstyle i-1//)으로 구할 수 있다.


누적합 수열의 성질

1. //(S\scriptstyle 0\textstyle=0//)
2. //(S\scriptstyle 1\textstyle= a\scriptstyle1//)
3. //(S\scriptstyle i\textstyle=S\scriptstyle i-1\textstyle + a\scriptstyle i//)

4. //(S\scriptstyle n\textstyle=a\scriptstyle 1\textstyle +a\scriptstyle 2\textstyle + ... + a\scriptstyle n-1\textstyle + a\scriptstyle n//)

5. //(a\scriptstyle i\textstyle +a\scriptstyle i+1\textstyle + ... + a\scriptstyle j-1\textstyle + a\scriptstyle j\textstyle= S\scriptstyle j\textstyle - S\scriptstyle i-1//)


누적합을 활용한 빠른 구간합 계산

위의 누적합 수열의 성질 중 5번째 성질을 이용해서 연속된 임의의 구간의 합을 //(O(1)//)의 시간복잡도에 구할 수 있다. 다만 유의해야 할 사항은, 코드로 옮길 때, 누적합의 값이 산술 오버플로(Arithmetic Overflow)가 나타날 수 있으므로 변수 크기를 잘 조절해야 한다.
누적합을 이용해서 페스티벌 문제를 풀어본 코드이다.

페스티벌 문제 풀이 코드


 

#include <stdio.h> #include <stdlib.h> void dump_arr(int* arr, int len) { for (int i=0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } int main() { int case_num, total_num, least_len, i, j, k, start, len, sum; int* arr, *summation; double min, current; scanf("%d", &case_num); for (i=0; i< case_num; i++) { scanf("%d %d", &total_num, &least_len); arr = (int*) calloc(sizeof(int), total_num); summation = (int*) calloc(sizeof(int), total_num + 1); summation[0] = 0; for (j=0; j< total_num; j++) { scanf("%d", &arr[j]); summation[j+1] = summation[j] + arr[j]; } min = 9999999999; int start, end; for (start = 0; start <= total_num - least_len; start++) { for (end = start + least_len ; end <= total_num; end++) { current = (double)(summation[end] - summation[start]) / (end-start); if (min > current) { min = current; } } } printf("%.10f\n", min); free(arr); free(summation); } }


2차원 누적합

위에서 언급한 누적합의 개념을 2차원으로 확장시킬 수 있다. 2차원 누적합으로 해결할 수 있는 문제의 예시로는 다음 Star sky 문제가 있다.


누적합이 2차원으로 확장되면서 일차원 배열은 2차원 배열로 확장되고, 누적합 수열도 2차원 배열형태로 확장된다.

직사각형 형태의 배열에 포함되는 직사각형 구간의 모든 원소의 합을 빠르게 구할 수 있게 된다.


다음과 같이 2차원 배열 //(a(i, j)//)과 2차원 누적합 배열 //(S(i, j)//)가 있다고 가정해보자.


가장 왼쪽 위를 //(a(1, 1)//)라고 할 때 //(S(i,j)//)는 //(a(1,1)//)와 //(a(i,j)//)를 양 대각 끝 꼭지점으로 하는 직사각형 범위 면적 안의 모든 //(a//) 원소의 합으로 정의된다.




//(a(2,2) ~ a(3,3)//)의 부분합을 구하기 위해서는 //(S(3,3)//)에서 시작한다. 

//(S(3,3)//)값에서 //(S(1,3)//)값을 빼고, //(S(3,1)//) 값을 빼면 초록색 부분의 합에서 주황색 부분의 합이 빠진 값이 된다

. 왜냐하면 //(S(1,3)//)값에 주황색 부분이 포함되어 있고, //(S(3,1)//)부분에도 주황색 부분의 값이 포함되어 있으니, 두번 빼진 셈 이다. 

따라서 다시 주황색 부분에 해당하는 //(S(1,1)//)를 더해주면 초록색 부분의 구간합이 된다.


//((sx,sy)//)부터 //((dx,dy)//) 까지의 합을 //(Range(a,b,c,d)//)라고 할 때 이를 //(S(i,j)//)로 나타내면 다음과 같다.

//(Range(a,b,c,d)= S(dx,dy)- S(sx-1,dy)- S(dx,sy-1)+ S(sx-1,sy-1)//)


위 수식을 이용해서 직사각형 형태의 영역의 구간합을 //(O(1)//)의 시간복잡도에 구할 수 있다.

다만 구현 시 유의해야 할 점은, 정수 오버플로에 유의해야 한다. 

1차원 구간합 수열의 경우 //(S\scriptstyle 0\textstyle=0//)인데, 

2차원 구간합 수열의 경우 //(S(0,x)//)와 //(S(x,0)//)와 같은 형태로 나타나는 수열의 값은 모두 //(0//)이다.

따라서 해당 값에 0으로 값이 잘 들어가도록 하여 실수를 방지해야 한다.


2차원 구간합 수열을 구하는 방법은 Row 방향으로 한번 1차원 구간합을 모두 다 구한 뒤, Column 방향으로 구해진 구간합을 다시 구간합 하면 된다.

//(a(i,j)//)에서 Row(가로)방향으로 누적합을 구하여서 //(s(i,j)//) 수열이 되고, 해당 수열을 Column(세로)방향으로 누적합을 구해서 최종적인 //(S(i,j)//) 수열이 된다.




Star sky 문제 풀이 코드

#include <iostream> #include <vector> #include <cstdio> using namespace std; int map[101][101][11]; //x좌표 / y좌표 / 시작밝기 -> 누적합 버전 int main() { int n, q, c; //별의 개수, 별 바라보는 회수, 별의 최대밝기 cin >> n >> q >> c; for (int i=0; i < n; i++) { int a,b,c; cin >> a >> b >> c; map[a][b][c]++; } for (int s=0; s < 11; s++) { for (int x=0; x < 101; x++) { for (int y=0; y < 100; y++) { map[x][y+1][s] += map[x][y][s]; } } } for (int s=0; s < 11; s++) { for (int x=0; x < 101; x++) { for (int y=0; y < 100; y++) { map[y+1][x][s] += map[y][x][s]; } } } while(q--) { int t, x1, y1, x2, y2; cin >> t >> x1 >> y1 >> x2 >> y2; int sum=0; for (int src_bright = 0; src_bright < 11; src_bright++) { int add = (src_bright + t) % (c+1); int num = map[x2][y2][src_bright] - map[x1-1][y2][src_bright] - map[x2][y1-1][src_bright] + map[x1-1][y1-1][src_bright]; sum += add*num; } cout << sum << endl; } }


복잡도 분석


누적합을 사용하는데에는 2단계의 Step이 있다.

1. 전처리(Preprocessing) : 누적합 수열 //(S(i)//) 혹은 //(S(i,j)//)를 구한다.

2. 계산 : 원하는 구간의 구간합을 계산한다.



전처리 단계의 경우 1차원 누적합일 경우 //(O(N)//)의 시간복잡도와 공간 복잡도를 갖는다.

2차원 누적합은 전처리 단계에서 //(O(N^2)//)의 시간복잡도와 공간 복잡도를 갖는다.


계산 단계의 경우 차원과 관계없이 //(O(1)//)의 상수 시간복잡도를 갖는다.


누적합의 한계

누적합의 경우에는 합을 구하는 도중에 원본 수열값이 변하는 경우 누적합 수열을 다시 계산해야하는 단점이 있다. 따라서 원본 수열값이 변할 수 있는 경우에 구간 합을 빠르게 구해야 할 경우에는 세그먼트 트리를 사용해야 한다.


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

백준저지 2133번 문제 타일 채우기 문제 풀이입니다.


전형적인 dp문제로 부분문제로 나누어서 풀 수 있습니다.


저 같은 경우는 dp[len][state]로 나누었는데 len 이전까지의 타일의 모든 칸은 이미 채워졌다고 가정하고 len번째 column(열)의 타일은 state 형태로 채워져 있다고 부분문제를 정의했습니다.


새로 행은 3칸이 있으므로 총 2^3로 8가지의 상태가 있을 수 있고, 이를 비트마스크로 표현했습니다.


기존 모양에서 가장 왼쪽 칸을 모두 채우는 경우의 수를 따져보면 위와 같이 나타납니다.

위의 경우를 구현한 코드는 다음과 같습니다.



 

#include <bits/stdc++.h>
using namespace std;

int dp[31][8]; //dp[len][state]
int main() {
    int n;
    cin >> n;
    dp[0][0] = 1;
    for (int i=0; i < n; i++) {
        auto& c = dp[i];
        auto& n = dp[i+1];
        n[4] += c[0];
        n[1] += c[0];
        n[7] += c[0];
        
        n[0] += c[1];
        n[6] += c[1];
        
        n[5] += c[2];
        
        n[4] += c[3];
        
        n[0] += c[4];
        n[3] += c[4];
        
        n[2] += c[5];
        
        n[1] += c[6];
        
        n[0] += c[7];
    }
    
    cout << dp[n][0] << endl;
	return 0;
}




나눗셈


정수 두 개를 나누었을 때, 몫과 나머지를 구할 수 있다.


//( A/B = Q\space remainder \space R //)


이때 A는 피제수(dividend)라고 부른다.

B는 제수(divisor) 라고 부른다.

Q는 몫(quotient)라고 부른다.

R는 나머지(remainder)라고 부른다.


모듈러(Modulo) 연산

나머지 연산은 modulo라고 하며 mod라고 나타낸다.


//( 7\space mod\space 2 = 1 //) 이다. 왜냐면 7을 2로 나누면 몫이 3이고 나머지가 1이므로, 이 나머지 값이 모듈러 연산의 결과값이 된다.


그렇다면 //( -5\space mod\space 3 //) 과 같은 경우는 어떻게 될까?


이러한 경우는 모듈러 연산의 특징을 이용해서 값을 구할 수 있다.

모듈러 연산은 피연산자의 값을 주기로 같은 결과값을 갖는 주기성을 띤다. 따라서 다음과 같은 관계가 성립한다.


//( -5\space mod\space 3 = -2\space mod\space 3 = 1\space mod\space 3 = 4\space mod\space3 ...//)


즉 첫번째 피연산자에 두번째 피연산자의 정수배의 값을 더해도 같은 값이 나오는 것을 이용하여 문제를 해결할 수 있다.

다시말해 -5에 3이나 6이나 9 등의 //(3*K//)의 값을 더해도(단 //(K//)는 정수) 결과값은 같다는 것이다.


모듈러 합동(Congruence Modulo)

이제 모듈러 합동(Congruence Modulo)에 대해 알아보자.


다음과 같은 수식이 있다.


//(A \equiv B(modC)//)


이를 A는 모듈러 C에 대해서 B와 합동이다 라고 읽는다. (A is congruent to B modulo C)

이는 다르게 말하면, //( A\space mod\space C = B\space mod\space C //)라는 뜻이다.


위 수식이 성립한다면, 또한 //(A-B//)는 //(C//)의 정수배라고 볼 수 있다.



모듈러 역원(Modular inverse)

역원(inverse)


역원에 대해 한번 다시 알아보자. 어떤 수와 그 수의 역원을 곱하면 1이 된다.

숫자 //(A//)의 역원은 //(1/A//)이다, 왜냐면 //(A * 1/A = 1//)이기 때문이다.


0보다 큰 실수는 역원을 갖는다.

그리고 //(A//)의 역원과 곱셈을 하는 것은 //(A//)로 나누는 것과 같다.


모듈러 역원(Modular inverse)

모듈러 연산은 산술적으로 나눗셈 연산이 없다. 하지만 모듈러는 역원이 존재한다.
//(A \space (mod\space C)//)의 역원은 //(A^{-1}//) 이다.
이 때, //(A*A^{-1} \equiv 1(mod\space C) //)이다.

이는 달리 말하면 //(A//)의 역원 //(A^{-1}//)는 //((A*A^{-1})\space mod\space C = 1//) 를 성립하는 //(A^{-1}//) 값을 뜻한다.

이때 이를 나머지 연산의 곱셈 역원이라고도 부른다.
//(A^{-1}//)는 정수 //(A//)를 //(C//)로 나눈 나머지 연산의 곱셈 역원이다.

이 역원은 //(A//)와 //(C//)가 서로소(coprime)인 경우에만 존재한다.


모듈러 역원의 활용

이 모듈러 역원을 어디다가 쓰는지 궁금할 수 있다. Problem Solving에서 자주쓰이는 경우는 다음과 같은 경우이다.

다음 성질이 자주 쓰인다.

//( \frac A B \mod C \equiv (A \mod C) * (B^{-1} \mod C) //)



프로그래밍을 할 때, //(A//)를 구하는 과정에서 //(A//)가 int나 long long 범위를 벗어나서, 온전한 //(A//)값을 구할 수 없고, //( A \mod C //) 값만 구할 수 있는 경우

위의 성질을 활용하면 간편하다.


//(A//)값과 //(B^{-1}//)를 각각 구해서 모듈러를 취한 뒤, 곱한 뒤 다시 모듈러를 취하면 되기 때문이다.



그러면 //(B^{-1}//)를 어떻게 구하느냐면, 페르마의 소정리를 이용하면 된다.





-------------------------

[참고]

https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/e/congruence-relation

https://www.acmicpc.net/blog/view/29

백준저지 1013번 문제 Contact과 2671번 문제 잠수함 식별은 문제가 똑같다.


특정한 문자열 패턴에 일치하는지 확인하는 문제이다. 문자열 패턴이 정규표현식(Regular Expression) 형태이므로 DFA(Deterministic Finite Automata)를 그려서 O(n)에 문제를 해결할 수 있다.


주어진 패턴이 (100+1+|01)+인데, DFA를 바로 그리기에는 어려우므로 NFA(Non-deterministic Finite Automata)를 먼저 그린다. 이후 DFA로 변환해보도록 한다.


아래 그림은 NFA를 그린 것이다. Initial State는 a이고, Final State는 e, g이다.


이후 DFA로 변환하면 다음과 같다. 똑같이 Initial state는 a이고 Final State는 g, e, {b,e} 이다.

state를 빨간샛 숫자에 맞추어 번호를 부여한 뒤 구현하면 다음과 같은 코드가 나타난다.


1013번 Contact 문제 풀이 코드이다.

 

#include <bits/stdc++.h>
using namespace std;

#define FAIL 9
const int tr[10][2] = {
    {5, 1}, //0
    {2, FAIL}, //1
    {3, FAIL}, //2
    {3, 4}, //3
    {5, 7}, //4
    {FAIL, 6},//5
    {5, 1},//6
    {8, 7},//7
    {3, 6},//8
    {FAIL, FAIL}//9
};

bool chk(string &seq) {
    int state = 0;
    for (size_t i=0; i < seq.size(); i++) {
        char ch = seq[i] - '0';
        state = tr[state][ch];
    }
    return state == 4 or state == 6 or state == 7;
}
int main() {
    int t;
    cin >> t;
    while(t--) {
        string seq;
        cin >> seq;
        bool ans = chk(seq);
        cout << (ans ? "YES" : "NO") << '\n';
        
    }
	return 0;
}




잠수함 식별 문제도 거의 동일한 코드로 풀이가 가능하다. 하지만 2671번 잠수함 문제는 C++에서 제공하는 정규표현식 라이브러리를 이용해서 풀이해보겠다.

 

#include <bits/stdc++.h>
using namespace std;

int main() {
    regex re("(100+1+|01)+");    
    cmatch m;
    string seq;
    cin >> seq;
    bool ans = regex_match(seq.c_str(), m, re);
    cout << (ans ? "SUBMARINE" : "NOISE") << '\n';

	return 0;
}




위와 같은 간단한 코드로 쉽게 정규표현식에 일치하는지 확인할 수 있다.


정규표현식이 정확한지는 다음 사이트에서 쉽게 태스트 해 볼 수 있다.

https://regexr.com/

한국정보올림피아드(정올) 1996년도 초등부 문제 풀이 포스팅이다.


문제들은 백준저지에서 참고하고 풀이했다.

https://www.acmicpc.net/category/detail/403


1번 단지번호 붙이기 문제이다.

https://www.acmicpc.net/problem/2667 (백준 2667번)


dfs로 풀면 간단하게 풀 수 있는 문제이다.

 

#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

vector<vector<char> > matrix;
int N; //length of matrix edge
int num_of_danji;
int num_of_1;
int x,y;
vector<int> num_of_houses;
void dump_matrix();
bool find_danji();
bool spread(int xpos, int ypos);
bool comp(int a, int b) {
    return (a > b);
};
int main() {
	char temp;
	scanf("%d\n", &N);
	
	matrix.assign(N, vector<char>(N, '0'));
	for (int i=0; i<N; i++) {
		for (int j=0; j<N; j++) {
			scanf("%c", &temp);
			if (temp == '1') {
				num_of_1++;
			}
			matrix[i][j] = temp;
		}
		scanf("\n");
	}

	while (num_of_1 > 0) {
		// dump_matrix();
		find_danji();
		int temp = num_of_1;
		// cout << "x:" << x << endl << "y:" << y << endl;
		spread(x, y);
		num_of_danji++;
		num_of_houses.push_back(temp - num_of_1);
		// dump_matrix();
	}
	
	cout << num_of_danji << endl;
	sort(num_of_houses.begin(), num_of_houses.end());
	for (int i=0; i< num_of_houses.size(); i++) {
		cout << num_of_houses[i] << endl;;
	}
	
}

bool find_danji() {
	for (int i=0; i<N; i++) {
		for (int j=0;j<N; j++) {
			if (matrix[i][j] == '1') {
				x = i;
				y = j;
				return true;
			}
		}
	}
	x = -1;
	y = -1;
	return false;
}
bool spread(int xpos, int ypos) {
	// cout << "spread(" << xpos << "," << ypos << ")" << endl;
	if (xpos >= 0 && xpos < N && ypos >= 0 && ypos < N) {
		// cout << "flag 1"<< endl;
		if (matrix[xpos][ypos] == '1') {
			// cout << "flag 2"<< endl;
			matrix[xpos][ypos] = '0';
			num_of_1--;
			
			return spread(xpos-1, ypos) || spread(xpos, ypos-1) || spread(xpos+1, ypos) || spread(xpos, ypos+1);
		} else {
			return false;
		}
	} else {
		return false;
	}
}
void dump_matrix() {
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			printf("%c", matrix[i][j]);
		}
		putchar('\n');
	}
}




2번 숫자고르기 문제이다.

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

다음 특징들에 착안해서 알고리즘을 고안했다.

위쪽의 그룹의 숫자는 중복이 없고 인덱스값과 같다.

아래쪽 그룹의 숫자는 중복이 있다. 따라서 없는 숫자도 있다.


위쪽 그룹 숫자를 U집합 이라고 하고 아래쪽 집합 숫자를 D집합이라고 하자.


아래쪽 그룹에 없는 숫자가 위쪽 그룹에서 지정될 수 없다는 점을 이용해서 다음 알고리즘을 고안했다.


1. 두번째 줄의 숫자 집합을 구한다. 이를 A라고 한다.

2. A의 원소를 인덱스(첫번째 줄의 숫자기준)로 하는 숫자 집합 B를 구한다.

3. A집합과 B집합이 일치하면 이(A집합 혹은 B집합)를 리턴한다. 그렇지 않으면 다음 단계로 간다.

4. B집합을 A집합으로 두고, B집합은 초기화한다. A집합을 기준으로 스텝 2를 다시 시도한 뒤 3번으로 간다.


백준에 나타난 예시에서 해당 알고리즘을 진행하면 다음과 같이 나타난다.


첫번째 스탭에서 집합 A는 다음과 같이 나타난다.

A = { 1, 3, 4, 5, 6 }

아래쪽 집합의 숫자들을 중복을 제거한 집합의 결과이다.

이때 이 A집합은 U집합에서 고른녀석들이라고 볼 수 있다.


알고리즘 두번째 스탭에서 집합 B를 구한다.

집합 B는 다음 항목들의 집합이다.

D[1], D[3], D[4], D[5], D[6]

B = { 3, 1, 5, 5, 6 } = { 1, 3, 5, 6 }

이때 이 B집합은 D집합에서 A집합에 대응되는 녀석이라고 볼 수 있다.


세번재 스탭에서 A와 B집합을 비교했을 때,  B집합이 크기가 더 작으므로 B집합을 A집합에 대입하고 다시 이어나간다.


A집합은 다시 다음값이 된다.(U집합에서 고른녀석들)

A = { 1, 3, 5, 6 }


B = { 3, 1, 5, 4 } = { 1, 3, 4, 5 }

다시 비교했을 때 맞지 않으므로 A집합에 다시 B 집합을 대응한다.


A = { 1, 3, 4, 5 }

B = { 3, 1, 5, 5 } = { 1, 3, 5 }


일치하지 않으므로 다시 대응한다.

A = { 1, 3, 5 }

B = { 1, 3, 5 }

일치하므로 정답을 리턴한다.


 

#include <stdio.h>
#include <vector>
#include <iostream>
#include <set>

using namespace std;

vector<int> arr; //기본애들
vector<int> elite; //뽑힌애들
set<int> set1, set2;
int N;

void arr_dump();
void set_dump(set<int> a);
bool is_equal(set<int>, set<int>);
int main() {
	
	int temp;
	scanf("%d\n", &N);
	arr.reserve(N+1);
	arr.push_back(0000);
	for (int i=0; i<N; i++) {
		scanf("%d\n", &temp);
		arr.push_back(temp);
	}
	
	// arr_dump();
	for(int i=1; i< arr.size(); i++) {
		set1.insert(arr[i]);
	}
	// cout << "set1 : ";
	// set_dump(set1);
	for (set<int>::iterator pos = set1.begin(); pos != set1.end(); pos++) {
		set2.insert(arr[*pos]);
	}
	// cout << "set2 : ";
	// set_dump(set2);
	// cout << "is_equal " << is_equal(set1, set2) << endl;
	

	do {
		set1 = set2;
		set2.clear();
		for (set<int>::iterator pos = set1.begin(); pos != set1.end(); pos++) {
			set2.insert(arr[*pos]);
		}
		
	} while(!is_equal(set1, set2));
	// cout << "fin";
	// set_dump(set1);
	printf("%lu", set1.size());
	set_dump(set1);
}

void arr_dump() {
	for (int i=1; i<arr.size(); i++) {
		printf("%d ", arr[i]);
	}
}
void set_dump(set<int> a) {
	cout << endl;
	for (set<int>::iterator IterPos = a.begin(); IterPos != a.end(); IterPos++) {
		cout << *IterPos << endl;
	}
}
bool is_equal(set<int> a, set<int> b) {
	if (a.size() != b.size()) {
		return false;
	}
	set<int>::iterator ai, bi;
	ai = a.begin();
	bi = b.begin();
	while (ai != a.end()) {
		if (*ai != *bi) {
			return false;
		}
		ai++;
		bi++;
	}
	return true;
}


3번 직사각형 네개의 합집합의 면적 구하기 문제이다.

https://www.acmicpc.net/problem/2669 (백준 2669번)


N값이 크지 않으므로 좌표평면을 만들어서 죄다 더해버리면 된다.


 

#include <vector>
#include <iostream>
#include <stdio.h>

using namespace std;

int map[100][100] = {0,};
int rect[4][4];

void dump();
int answer();
int main() {
	for (int i=0; i<4;i++) {
		scanf("%d %d %d %d\n", &rect[i][0], &rect[i][1], &rect[i][2], &rect[i][3]);
	}
	for (int i=0; i<4;i++) {
		for (int x=rect[i][0]; x < rect[i][2]; x++) {
			for(int y=rect[i][1]; y < rect[i][3]; y++) {
				map[x][y]++;
			}
		}
	}
	// dump();
	cout << answer() << endl;
}
int answer() {
	int count = 0;
	for(int i=0;i<100;i++) {
		for (int j=0;j<100;j++) {
			if (map[i][j]) {
				count++;
			}
		}
	}
	return count;
}
void dump() {
	//rect
	// for(int i=0;i<4;i++) {
	// 	for(int j=0;j<4;j++) {
	// 		printf("%d\t", rect[i][j]);
	// 	}
	// }
	
	for(int i=99;i>=0;i--) {
		for(int j=99;j>=0;j--) {
			printf("%d ", map[j][i]);
		}
		putchar('\n');
	}
}



한국정보올림피아드(정올) 2013년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1075


총 4문제가 있습니다.


1번 올림픽 문제입니다. (백준 8979번)

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


단순 계산으로 풀 수 있다. 비교 연산자를 재정의해서 정렬한 뒤, 중복 순위의 경우에만 체크를 해주면 된다.

 

#include <bits/stdc++.h>
using namespace std;
struct data {
    int num, gold, silver, bronze;
    
    bool operator < (const data& rhs) const {
        return gold != rhs.gold ? gold > rhs.gold : (silver != rhs.silver ? silver > rhs.silver : bronze > rhs.bronze);
    }
    bool operator == (const data& rhs) const {
        return gold == rhs.gold && silver == rhs.silver && bronze == rhs.bronze;
    }
};
vector<data> arr;
int main() {
    int n, k;
    cin >> n >> k;
    arr.reserve(n);
    for (int i=0; i < n; i++) {
        data tmp;
        scanf("%d %d %d %d\n", &tmp.num, &tmp.gold, &tmp.silver, &tmp.bronze);
        arr.push_back(tmp);
    }
    sort(arr.begin(), arr.end());
    
    int rank = 0;
    int accu = 1;
    data prev;
    prev.gold = prev.silver = prev.bronze = -1;
    //dump
    for (int i=0; i < n; i++) {
        auto& a = arr[i];
        if ( (prev == a) == false) {
            rank += accu;
            accu = 1;
        } else {
            accu++;
        }
        // printf("rank %d - ", rank);
        if (a.num == k) {
            cout << rank << '\n';
            return 0;
        }
        
        prev = a;
        // printf("(%d) %d, %d, %d\n", a.num, a.gold, a.silver, a.bronze);
    }
    //dump
}


2번 택배 문제입니다.

https://www.acmicpc.net/problem/8980 (백준 8980번)


그리디로 풀 수 있다. 그리디도 그리딘데, 어떤식으로 그리디를 적용시킬지가 중요하다. 일단 배달 구간이 가장 짧은놈들 순서대로 sorting한다. 배달 구간이 짧을수록 트럭에서 공간을 점유하는 시간이 작아지므로 그렇게 하는 것이다.    

그리고 배달구간이 짧은놈 순서대로 트럭에 최대한 집어넣으면 정답이 된다. 이것을 어떻게 구현하느냐에서 많이 해맸는데, 궂이 시간순대로 트럭이 이동하는 방식으로 시뮬레이션을 하지 않더라도 짧은 구간부터, 구간별 트럭에 적제된 화물양을 저장해놓았다가 최대한 적재시키는 식으로 정답을 구하면 쉽게 풀이가 된다.


 

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> pii; //src, dst
struct Order {
    int src, dst, size;
};

Order orders[10001];

bool cmp(const struct Order& lhs, const struct Order& rhs) {
    if (lhs.dst != rhs.dst) return lhs.dst < rhs.dst;
    return lhs.src < rhs.src;
}
int loaded[2001]; //해당 위치에 있을 때, 트럭에 차있는 양
int main() {
    int ans = 0;
    int n, c, m;
    cin >> n >> c >> m;
    for (int i=0; i < m; i++) {
        scanf("%d %d %d", &orders[i].src, &orders[i].dst, &orders[i].size);
        
    }
    
    sort(orders, orders + m, cmp);
    for (int i=0; i< m; i++) {
        auto& order = orders[i];
        int already_loaded_amount = 0;
        for (int pos = order.src; pos < order.dst; pos++) { //구간을 돌면서 가장 많이 적제된 양을 찾는다.
            if (loaded[pos] > already_loaded_amount) {
                already_loaded_amount = loaded[pos];   
            }
        }
        int can_be_more_load_amt = min(order.size, c - already_loaded_amount); //더 적재할 양을 고른다.
        for (int pos = order.src; pos < order.dst; pos++) { //구간에 적재 시킨다.
            loaded[pos] += can_be_more_load_amt;
        }
        ans += can_be_more_load_amt;
    }    
    cout << ans <<'\n';
    return 0;
}


3번 입력숫자 문제입니다.

https://www.acmicpc.net/problem/8981 (백준 8981번)


비슷한 로직으로 하면 역연산이 된다. 이전 값 만큼 offset을 앞으로 전진시키고, 해당 위치에 다음 받은 값을 쓴다. 만약 거기 이미 값이 있다면 값이 없을 때 까지 앞으로 전진하면 된다.

 

#include <bits/stdc++.h>
using namespace std;

int arr[30];
int input[30];
int ptr;
int val;
int n;
void search() {
    while(arr[ptr]) {
        ptr++;
        ptr %= n;
    }
}
int main() {
    cin >> n;
    cin >> val;
    arr[ptr] = val;
    for (int i=1; i < n; i++) {
        ptr += val;
        ptr %= n;
        search();
        cin >> input[i];
        arr[ptr] = val = input[i];
    }
    
    cout << n << endl;
    for (int i=0; i < n; i++) {
        cout << arr[i] << ' ';
    }
    cout << '\n';
	return 0;
}




한국정보올림피아드(정올) 2014년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1274


총 4문제가 있습니다.


1번 전자레인지 문제입니다. (백준 10162)

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


나눗셈 연산과 모듈러 연산을 이용하면 간단하게 해결할 수 있습니다.



 

#include <iostream>

using namespace std;

int main() {
	int t;
	cin >> t;
	if (t % 10 != 0) {
		cout << -1 << endl;
		return 0;
	}
	int m300, m60, m10;
	m300 = m60 = m10 = 0;
	
	m300 = t/300;
	t %= 300;
	m60 = t/60;
	t %= 60;
	m10 = t/10;
	cout << m300 << ' ' << m60 << ' ' << m10 << endl;
	return 0;
}


2번 색종이 문제입니다. (백준 10163)

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


N값이 작으므로, 2차원 배열로 종이 그리드를 모두 나타내고 시뮬레이션 한 뒤 개수를 세면 됩니다.



 

#include <iostream>

using namespace std;

int map[102][102];
int sz[101];
int n;
int main() {
	cin >> n;
	int sx, sy, dx, dy;
	for (int i=1; i <= n; i++) {
		cin >> sx >> sy >> dx >> dy;
		dx += sx;
		dy += sy;
		for (int x=sx; x < dx; x++) {
			for (int y=sy; y <dy; y++) {
				map[x][y] = i;
			}
		}
	}
	
	for (int i=0; i < 101; i++) {
		for (int j=0; j < 101; j++) {
			if (map[i][j]) {
				sz[ map[i][j] ]++;
			}
		}
	}
	for (int i=1; i <= n; i++) {
		cout << sz[i] << endl;
	}
}


3번 격자상의 경로 문제입니다. (백준 10164)

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


다이나믹 프로그래밍으로 문제를 해결할 수 있습니다. K값이 하나 뿐이므로, (시작점 -> K까지 경로)와 (K지점 -> 도착점까지 경로)를 나누어서 구해주면 된다. 숫자를 1부터 세므로 이에 따른 오류를 조심해야 한다.


get함수를 쓸 때 dp[x][y]는 시작 지점에서 (x, y)까지 도달 할 수 있는 경로의 개수를 뜻한다.

get2 함수를 쓸 때에는, (limitx, limity)에서 (x, y)로 도달할 수 있는 경로의 개수를 뜻한다.

get함수가 호출되었을 때에는 K지점을 기점으로 왼쪽과 위쪽부분의 부분 사각형에만 값이 들어가 있으며 또한 get2함수에서 (limitx, limity)보다 위쪽이나 왼쪽은 0으로 예외처리가 되어있게 처리되어있다.


 

#include <iostream>

using namespace std;
int dp[16][16];

int n, m, k;

int get(int x, int y) {
	if (x < 0 || y < 0) return 0;
	if (x == 0 && y == 0) return 1;
	if (dp[x][y]) return dp[x][y];
	
	return dp[x][y] = get(x-1, y) + get(x, y-1);
}

int get2(int x, int y, int limitx, int limity) {
	if (x < limitx || y < limity) return 0;
	if (dp[x][y]) return dp[x][y];
	
	return dp[x][y] = get2(x-1, y, limitx, limity) + get2(x, y-1, limitx, limity);
}
int main() {
	cin >> n >> m >> k;
	
	if (!k) {
		cout << get(n-1, m-1) << endl;
	} else {
		int xpos = (k-1) / m;
		int ypos = (k-1) % m;
		get(xpos, ypos);
		cout << get2(n-1, m-1, xpos, ypos) << endl;
	}
}


4번 버스 노선 문제입니다. (백준 10165)

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


케이스를 잘 나누어야 한다. 일단 노선이 직선일 경우에 대하여 생각을 해 보자. 비교 연산자를 재정의해서 소팅해서, 시작위치는 오름차순으로, 끝 위치는 내림차순으로 정렬한다.    

그러면 소팅을 하고 나서 선형탐색을 앞에서 뒤로 할 때, 뒤에 나타난 녀석이 앞에 있는 녀석을 포함하는 경우는 없다. 

시작위치는 뒤로 갈 수록 앞으로 가기 때문에 시작 범위는 줄어들기 마련이다.    

그리고 시작 위치가 고정인 상태에서는 끝나는 위치는 계속해서 작아지게 되어있으므로, 시작 위치가 다음 값이 되었을 때 끝 위치가 가장 큰놈 일 것이다. 이러한 점을 이용해서 끝놈 위치만 저장해 놓고, 끝놈보다 작으면 포함시켜버리고, 끝놈보다 크면, 그 끝놈 위치를 새 끝놈 위치로 저장해서 끝만 비교하면서 선형 탐색을 하면 된다.    


그러면 또 노선이 원형일 경우를 체크를 해야 하는데, 시작점<끝점 의 경우 A그룹으로 정의하고 시작점>끝점 의 경우를 B그룹으로 정의를 하자. B그룹의 경우는 0번위치를 무조건 통과하고 A그룹은 0번 위치를 지나갈 수는 없다. 따라서 A그룹이 B그룹을 포함할 수는 없다. 

B그룹에서 시작 부분의 최대값을 구하고, 끝 부분의 최소값을 구해서 A그룹의 (?~시작부분 최대값) 의 경우와 (끝부분 최소값~?)을 모두    

포함할 수 있다. 이러한 경우를 체크해 주고(B가 A를 포함하는 경우) A그룹이 A를 포함하는 경우와 B그룹이 B를 포함하는 경우는 직선에서 비교하는 것과 똑같은 방법으로 체크하면 된다.

#include <bits/stdc++.h> using namespace std; int n, m; struct Route { int src, dst, num; }; bool cmp(const struct Route& lhs, const struct Route& rhs) { if (lhs.src != rhs.src) return lhs.src < rhs.src; return lhs.dst > rhs.dst; } vector<Route> small, large; //인덱스 숫자가 작을수록 범위가 큰놈이다. vector<bool> ans; int main() { cin >> n >> m; ans.reserve(m+1); int large_over = -1; int large_below = n+1; int over = 0; for (int i=1; i<=m; i++) { Route tmp; scanf("%d %d", &tmp.src, &tmp.dst); tmp.num = i; if (tmp.src < tmp.dst) { small.push_back(tmp); } else { large_over = max(large_over, tmp.dst); large_below = min(large_below, tmp.src); large.push_back(tmp); } } sort(small.begin(), small.end(), cmp); sort(large.begin(), large.end(), cmp); //초기 large_over값은 large가 small 덮어쓰는 경우(0~over 범위로 덮는 경우임) //초기 large_below값은 large가 small 덮어쓰는 경우(below~N) 범위로 덮는 경우 //진행하다보면 small이 small 덮어쓰는 경우도 있음 for (size_t i=0; i < small.size(); i++) { if (small[i].dst <= large_over) ans[small[i].num] = true; else if (small[i].src >= large_below) ans[small[i].num] = true; if (small[i].dst <= over) { ans[small[i].num] = true; } else { over = small[i].dst; } } over = -1; //large가 large 덮어쓰는 경우 for (size_t i=0; i < large.size(); i++) { if (large[i].dst <= over) ans[large[i].num] = true; else over = large[i].dst; } for (int i=1; i <= m; i++) { if (!ans[i]) { cout << i << '\n'; } } return 0; }


한국정보올림피아드(정올) 2017년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1775


총 4문제로 이루어져 있는데, 제가 풀 때 당시에는 백준저지에는 3문제만 올라와있어서 3문제에 대한 코드만 올립니다.


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


2번, 방 배정하기 문제입니다. (백준저지 14697)


N값이 작으므로 3중 포문으로 O(N^3)으로 완전탐색으로 Naive하게 풀었습니다.


#include <bits/stdc++.h>
using namespace std;

int main() {
    int a, b, c, total;
    cin >> a >> b >> c >> total;
    
    int a_lim = total/a;
    int b_lim = total/b;
    int c_lim = total/c;
    for (int i=0; i <= c_lim; i++) {
        int cur = c * i;
        for (int j=0; j <= b_lim; j++) {
            int cur2 = cur + j*b;
            for (int k=0; k <= a_lim; k++) {
                int cur3 = cur2 + k*a;
                if (cur3 == total) {
                    cout << 1 << '\n';
                    return 0;
                }
            }
        }
    }
    cout << 0 << '\n';
	return 0;
}


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

3번, 서울에서 경산까지 문제입니다. (백준저지 14863)


DFS를 통한 완전탐색으로 풀되, 중복되어 풀리는 문제들(Overlapping-subproblems)는 Memoization 패턴을 이용하여 중복해서 풀리지 않도록 처리했습니다. 그리고 마지막 구간까지 도달하지 못하는 경우에 대해서 제외하도록 처리해야하는 것이 좀 까다로웠습니다.



#include <bits/stdc++.h>
using namespace std;

#define IMPOSSIBLE -987654321
typedef long long ll;
int dp[101][100001]; //[range][minute left]
int walk_min[101];
int walk_get[101];
int bicy_min[101];
int bicy_get[101];
int n, k;
int get(int segment, int minute_left) {
    if (segment >= n) return 0;
    if (minute_left <= 0) return IMPOSSIBLE;
    int& ret = dp[segment][minute_left];
    if (ret != -1) {
        return ret;
    };
    
    ret = IMPOSSIBLE;
    if (minute_left >= walk_min[segment] and get(segment+1, minute_left - walk_min[segment]) != IMPOSSIBLE) {
        ret = max(ret, get(segment+1, minute_left - walk_min[segment]) + walk_get[segment]);
    }
    if (minute_left >= bicy_min[segment] and get(segment+1, minute_left - bicy_min[segment]) != IMPOSSIBLE) {
        ret = max(ret, get(segment+1, minute_left - bicy_min[segment]) + bicy_get[segment]);
    }
    return ret;
    
}

int main() {

    scanf("%d %d", &n, &k);
    for (int i=0; i < n; i++) {
        scanf("%d %d %d %d", &walk_min[i], &walk_get[i], &bicy_min[i], &bicy_get[i]);    
    }
    memset(dp, -1, sizeof(dp));
    
    cout << get(0, k) << '\n';
	return 0;
}


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

4번, 줄서기 문제입니다. (백준저지 14864)


개인적으로 난이도가 꽤나 있었고, 완전 Nice한 해답은 아니라고 생각합니다.

일단 순서쌍에서 알아낼 수 있는 정보는 다음과 같습니다.

1. 나보다 뒤에 있는 놈에 대해서, 내가 그 놈보다 큰지 아닌지를 확인할 수 있다.

2. 나보다 앞에 있는 놈 중에 나보다 큰 놈이 몇 개인지 알 수 있다.    


물론 나보다 앞에 있는 놈 중에 큰놈이 몇개인지 뿐만 아니라 누군지까지 확인할 수 있지만, 일단은 저 두개를 이용해서 문제를 풀어보았습니다.

맨 뒤에서 부터 채워가면서 숫자를 넣을 것인데, 일단 집합 S를 다음과 같이 초기화 합니다. S = {1, 2, 3, ... , N}

맨 뒤에있는 숫자 입장에서는 나머지 모두가 나보다 앞에 있으므로, 나머지 수 중 자신보다 큰 수가 몇개인지 알 수 있습니다.

그러면 앞에있는 수 중 자신보다 큰 수의 개수를 k라고 하면, 집합 S에서 k번째 큰 수를 빼서 자신의 값에 집어넣습니다.


이런식으로 맨 앞까지 진행합니다.


이러면 일단 조건을 만족할만한 정답 셋을 하나 구할 수 있는데, Valid한 정답을 갖는다면 Unique한 정답을 가질 것입니다.

하지만 해당 인풋이 Invalid할 수도 있으니 한번 체크를 해야 합니다. (Invalid한 정답이라면 -1를 출력해야함)

이때 Strict하게 체크를 하려면 자신보다 뒤에 있는 숫자 중, 순서쌍에 해당된다면 자신보다 작은지, 순서쌍에 해당되지 않는다면 자신보다 큰지를 확인해야하는데, 둘다 체크하게 되면 TLE가 날 것입니다.   

특히나 순서쌍애 해당되지 않는 애들까지 체크를 하게 되면 O(N^2)이므로 꼼짝없이 TLE다. 하지만 순서쌍 개수는 100만개이므로, 순서쌍에 해당되는 경우만 체크할 경우 TLE는 안날 수 있습니다. 따라서 순서쌍에 해당되는 경우만 자신보다 작은 값을 갖는지만 체크하면 됩니다.


이 부분의 경우 -1이 정답인데 아니게 나올까봐 조마조마했는데 정답 판정이 났다. 아마 앞에서 만든 Answer Array가 주어진 순서쌍 조건이 맞는지만 확인해도빠진 조건이 있는 반례가 없다는게 논리적으로 맞아서 그런 것인지는 잘 모르겠습니다.

#include <bits/stdc++.h>
using namespace std;

#define NUM_STUDENT 100001 //학생수 최대
#define UNDEFINED 0 // 정답이 결정되지 않은 상태
set<int> larger_than_me[NUM_STUDENT], smaller_than_me[NUM_STUDENT];

int ans[NUM_STUDENT];
int n, m;
set<int> rest;
void fail() {
    cout << -1 << '\n';
    exit(EXIT_SUCCESS);
}
int main() {
    scanf("%d%d", &n, &m);
    
    //initialize rest set
    for (int i=1; i<=n;i++) {
        rest.insert(-i);
    }
    //initialize rest set
    
    if (n == 1) { //학생수 1명일때만 예외처리
        cout << 1 << '\n';
        return 0;
    }
    
    for (int i=0; i < m; i++) {
        int a, b;
        scanf("%d %d", &a, &b); // a > b라는 뜻
        smaller_than_me[a].insert(b); // a보다 작은 set에 b 추가.
        larger_than_me[b].insert(a);// b보다 큰 set에 a추가.
    }
    
    for (int cur = n; cur >= 1; cur--) {
        int larger_num = larger_than_me[cur].size();
        auto it = rest.begin();
        for (int i=0; i < larger_num; i++) {
            it++;
        }
        int kth_largest = -*it;
        ans[cur] = kth_largest;
        rest.erase(it);    
    }
    
    //check for validation - 주어진 조건은 맞는지 확인. 빠진 조건이 있는지는 안확인 ㅎㅎㅎ
    for (int cur = 1; cur < n; cur++) {
        int my_val = ans[cur];
        for (auto it : smaller_than_me[cur]) {
            int smaller_val = ans[it];
            if (smaller_val > my_val) {
                fail();
            }
        }
    }
    
    for (int i=1; i <= n; i++) {
        cout << ans[i] << ' ';
    }
    cout << '\n';
	return 0;
}


+ Recent posts