Codeforces Round 982 (Div. 2) 문제 해결 및 코드 분석

A 문제: 최적 직사각형 둘레

문제의 핵심은 최종 도형의 둘레가 최대 너비와 높이를 가진 직사각형의 둘레와 같다는 결론을 도출하는 것입니다.


#include 
using namespace std;
typedef long long ll;

void solve() {
    int test_case;
    cin >> test_case;
    
    while (test_case--) {
        int shape_count;
        cin >> shape_count;
        
        ll max_width = 0, max_height = 0;
        for (int i = 0; i < shape_count; i++) {
            ll w, h;
            cin >> w >> h;
            max_width = max(max_width, w);
            max_height = max(max_height, h);
        }
        
        cout << 2 * (max_width + max_height) << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

B 문제: 스탈린 정렬 최소 삭제

스탈린 정렬은 비내림차순 수열을 생성합니다. 각 원소 a_i를 유지하려면 a_i 앞에 있는 모든 수가 a_i보다 작아야 하며, 뒤에 있는 모든 수도 a_i보다 작거나 같아야 합니다. O(n^2) 시간 복잡도로 각 원소에 대해 필요한 삭제 횟수를 계산합니다.


#include 
using namespace std;
typedef long long ll;

const int MAX_N = 2005;

void solve() {
    int test_case;
    cin >> test_case;
    
    while (test_case--) {
        int n;
        cin >> n;
        
        vector<int> arr(MAX_N);
        for (int i = 1; i <= n; i++) {
            cin >> arr[i];
        }
        
        int min_deletions = n;
        for (int i = 1; i <= n; i++) {
            int deletions = i - 1;
            for (int j = i + 1; j <= n; j++) {
                if (arr[j] > arr[i]) {
                    deletions++;
                }
            }
            min_deletions = min(min_deletions, deletions);
        }
        
        cout << min_deletions << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

C 문제: 조건 변환과 DFS 탐색

문제의 조건을 변환하면, 1 ≤ i ≤ n일 때 a_i + i - 1 = |a|인 경우 크기를 a_i + i - 1 + i - 1로 조정해야 합니다. DFS를 사용하고 map으로 방문 여부를 표시하여 각 값이 최대 한 번만 탐색되도록 O(n) 시간 복잡도로 해결합니다.


#include 
using namespace std;
typedef long long ll;
typedef pair PLI;

const int MAX_N = 300005;

void solve() {
    int test_case;
    cin >> test_case;
    
    while (test_case--) {
        int n;
        cin >> n;
        
        vector<ll> a(MAX_N);
        vector<PLI> elements(MAX_N);
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            elements[i] = {a[i] + i - 1, i - 1};
        }
        
        sort(elements.begin() + 1, elements.begin() + n + 1);
        
        map visited;
        ll result = n;
        
        function dfs = [&](ll size) {
            if (visited.count(size)) return;
            visited[size] = true;
            result = max(result, size);
            
            if (size > elements[n].first) return;
            
            PLI temp = {size, 0};
            int left = lower_bound(elements.begin() + 1, elements.begin() + n + 1, temp) - elements.begin();
            
            temp.second = n;
            int right = upper_bound(elements.begin() + 1, elements.begin() + n + 1, temp) - elements.begin() - 1;
            
            for (int i = left; i <= right; i++) {
                if (elements[i].second == 0) continue;
                dfs(size + elements[i].second);
            }
        };
        
        dfs(n);
        cout << result << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

D1 문제: 동적 프로그래밍 접근

주어진 데이터 범위 nm ≤ 3e5에서 O(nm) 해법이 필요함을 관찰할 수 있습니다. DP를 사용하여 f[i][j]를 k 값이 i일 때 a에 남은 첫 번째 원소의 인덱스가 j인 최소 비용으로 정의합니다.


#include 
using namespace std;
typedef long long ll;

const int MAX_N = 300005;
const ll INF = 1e18;

void solve() {
    int test_case;
    cin >> test_case;
    
    while (test_case--) {
        int n, m;
        cin >> n >> m;
        
        vector<ll> prefix_sum(n + 1);
        for (int i = 1; i <= n; i++) {
            cin >> prefix_sum[i];
            prefix_sum[i] += prefix_sum[i - 1];
        }
        
        vector<ll> b(m + 1);
        for (int i = 1; i <= m; i++) {
            cin >> b[i];
        }
        
        vector dp(2, vector<ll>(n + 2, INF));
        dp[1][1] = 0;
        ll answer = INF;
        
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n + 1; j++) {
                dp[(i + 1) & 1][j] = INF;
            }
            
            for (int j = 1; j <= n; j++) {
                int k = upper_bound(prefix_sum.begin() + j, prefix_sum.begin() + n + 1, 
                                   b[i] + prefix_sum[j - 1]) - prefix_sum.begin();
                
                if (k > j) {
                    dp[i & 1][k] = min(dp[i & 1][k], dp[i & 1][j] + m - i);
                }
                
                dp[(i + 1) & 1][j] = min(dp[(i + 1) & 1][j], dp[i & 1][j]);
            }
            
            answer = min(answer, dp[i & 1][n + 1]);
        }
        
        cout << (answer == INF ? -1 : answer) << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    solve();
    return 0;
}

태그: 알고리즘 코드포스 동적프로그래밍 자료구조 그리디

7월 20일 09:07에 게시됨