2025-11-05 NOIP 모의 대회 2 후기

결론 짧게:

100+0+0+0 점수.

T1: 소 Z의 장갑

문제 설명

길이가 \(n\)인 배열 \(a\)와 길이가 \(m\)인 배열 \(b\)가 주어집니다.

이 배열에서 \(\min(n,m)\)개의 쌍 \(a_i, b_j\)를 매칭해야 합니다. 각 숫자는 한 번만 매칭할 수 있습니다.

매칭의 비용은 \(|a_i - b_j|\)이며, 매칭 그룹의 비용은 이들 중 최댓값입니다. 이 최댓값을 최소화해야 합니다.

대회 당시

탐욕법 접근이 틀렸음을 인식하고 이진탐색으로 전환했습니다.

해결 방법

먼저, 탐욕법은 틀렸습니다. 이는 상한을 설정할 때만 적용할 수 있습니다.

최솟값을 찾기 위해 이진탐색을 사용합니다.

'체크' 함수를 통해 중간값이 유효한지 검증합니다.

정렬 후, 현재 수와 가장 가까운 수를 매칭시켜주는 방법을 사용합니다. 이 때, 더 큰 수로는 같은 위치를 매칭할 수 없습니다.

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f3f3f
using namespace std;

int n, m;
long long arrA[100010];
long long arrB[100010];

bool isValid(long long mid) {
    int ptrB = 1;
    int count = 0;
    for (int ptrA = 1; ptrA <= n; ptrA++) {
        while (ptrB <= m && abs(arrA[ptrA] - arrB[ptrB]) > mid) {
            ptrB++;
        }
        if (ptrB == m + 1) break;
        ptrB++;
        count++;
    }
    return count == n;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> arrA[i];
    for (int i = 1; i <= m; i++) cin >> arrB[i];
    sort(arrA + 1, arrA + 1 + n);
    sort(arrB + 1, arrB + 1 + m);

    if (n > m) {
        swap(n, m);
        swap(arrA, arrB);
    }

    long long left = 0, right = 2e9;
    while (left < right) {
        long long mid = (left + right) >> 1;
        if (isValid(mid)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    cout << left << "\n";

    #ifndef ONLINE_JUDGE
    cerr << "\n 사용 시간: " << clock() * 1.0 / CLOCKS_PER_SEC << "초.\n";
    #endif
    return 0;
}

T2: 소 Z의 문자열

문제 설명

어떤 문자열을 주어받았을 때, 인접한 두 문자를 교환할 수 있습니다. 인접한 두 문자가 같은 것이 없도록 만들기 위해 최소의 교환 횟수를 구하세요.

대회 당시

넘어쳤습니다. 아무것도 작성하지 않았습니다.

해결 방법

\(dp_{i,j,k,l}\)를 통해 \(i\)번째 위치까지 고려했을 때, \(j\)개의 \(0\)와 \(k\)개의 \(1\)를 배치했고 마지막 문자가 \(l\)일 때의 최소 이동 횟수를 저장합니다.

이때, 상태 전환은 분명치 않습니다.

마지막에 mhh의 기록을 참고했습니다.

공간이 부족할 수 있어 첫번째 차원을 제거해야 합니다.

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define INFLL 0x3f3f3f3f3f3f3f3f
using namespace std;

int n;
int a[410];

vector<int> vec[3];

int dp[2][410][410][3]; // 이전 i, j x 0, k x 1, 마지막은 l.

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    string s; cin >> s; n = s.length();
    for (int i = 1; i <= n; i++) a[i] = s[i-1] - '0';

    for (int i = 1; i <= n; i++) vec[a[i]].push_back(i);

    memset(dp[0], INF, sizeof dp[0]);

    for (int i = 0; i < 3; i++) dp[0][0][0][i] = 0;
    for (int i = 1; i <= n; i++) {
        memset(dp[i & 1], INF, sizeof(dp[i & 1]));

        for (int j = 0; j <= min(i, (int)vec[0].size()); j++) {
            for (int k = 0; k <= min(i, (int)vec[1].size()); k++) {
                int x = i - j - k;
                if (x > (int)vec[2].size()) continue;

                if (j) dp[i & 1][j][k][0] = min(dp[~i & 1][j-1][k][1], dp[~i & 1][j-1][k][2]) + abs(i - vec[0][j-1]);
                if (k) dp[i & 1][j][k][1] = min(dp[~i & 1][j][k-1][0], dp[~i & 1][j][k-1][2]) + abs(i - vec[1][k-1]);
                if (x) dp[i & 1][j][k][2] = min(dp[~i & 1][j][k][0], dp[~i & 1][j][k][1]) + abs(i - vec[2][x-1]);
            }
        }
    }

    int ans = INF;
    for (int i = 0; i < 3; i++) ans = min(ans, dp[n & 1][vec[0].size()][vec[1].size()][i]);
    if (ans == INF) cout << "-1\n";
    else cout << ans / 2 << "\n";

    #ifndef ONLINE_JUDGE
    cerr << "사용 시간: " << clock() * 1.0 / CLOCKS_PER_SEC << "초.\n";
    #endif
    return 0;
}

이상은 틀렸습니다.

呵呵, mhh의 방법이 hxf에 의해 해킹되었습니다.

그래서私も 그냥 패스했습니다.

더 자세히 알고 싶으면 JZ8의 블로그를 방문하세요.

요약

이번 대회는 정말 힘들었습니다. T3에서 선형 구조 분할을 사용한 사람들을 보면서 놀랐습니다.

태그: NOIP 모의대회 알고리즘 C++ 이진탐색

7월 31일 09:43에 게시됨