Codeforces 1000~1100 第三週の問題解説

部分文字列と部分配列

目的は、文字列 a と b を部分配列として含む最短の文字列を見つけることです。その長さは a と b の長さの合計から、両方で共通する文字数を引いたものです。a は必ず含まれるため、b の各文字が a で順番に現れるかをチェックします。

注意: 部分文字列は連続した文字列ですが、部分配列は順序を保ちつつ連続性は必要ありません。

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

void solve() {
    string a, b;
    cin >> a >> b;
    int result = a.size() + b.size();
    for (int start = 0; start < b.size(); ++start) {
        int overlap = 0, index = start;
        for (int i = 0; i < a.size() && index < b.size(); ++i) {
            if (b[index] == a[i]) {
                ++overlap;
                ++index;
            }
        }
        result = min(result, a.size() + b.size() - overlap);
    }
    cout << result << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

順列の出力

配列を奇数と偶数を交互に出力することで、分割されないようにします。奇数位置には昇順、偶数位置には降順で配置します。

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

void solve() {
    int n;
    cin >> n;
    vector<int> sequence(n);
    int even_index = 0, odd_index = 1;
    for (int i = 1; i <= n; ++i) {
        if (i % 2 == 1) {
            sequence[even_index] = i;
            even_index += 2;
        } else {
            sequence[odd_index] = i;
            odd_index += 2;
        }
    }
    for (int num : sequence) {
        cout << num << " ";
    }
    cout << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

モンスターの攻撃!

モンスターの移動距離が同じものはグループ化し、それぞれのグループに対して順番に処理します。一度に処理できる数を超える場合は "NO" を出力します。

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

void solve() {
    int n, k;
    cin >> n >> k;
    vector<long long> strength(n), distance(n);
    for (auto& s : strength) cin >> s;
    for (auto& d : distance) cin >> d;
    map<long long, long long> grouped_distances;
    for (int i = 0; i < n; ++i) {
        grouped_distances[abs(distance[i])] += strength[i];
    }
    long long remaining = k;
    for (const auto& [dist, total_strength] : grouped_distances) {
        if (total_strength > remaining) {
            cout << "NO\n";
            return;
        }
        remaining -= total_strength;
    }
    cout << "YES\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

非常に異なる配列

配列 a と b の長さが異なる場合でも、a の末尾と b の先頭をペアリングして最大の差分を求めます。b の残りの要素と a の先頭をペアリングして同様に計算し、最大値を選択します。

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

void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> a(n), b(m);
    for (auto& x : a) cin >> x;
    for (auto& x : b) cin >> x;
    sort(a.begin(), a.end());
    sort(b.begin(), b.end(), greater<>());
    long long max_difference = 0;
    for (int i = 0; i < n; ++i) {
        max_difference = max(max_difference, accumulate(a.begin() + i, a.end(), 0LL) + 
            accumulate(b.begin(), b.begin() + n - i, 0LL, [](long long sum, int val) { return sum - val; }));
    }
    cout << max_difference << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

合計ゲーム

Alice は最大の数を選び、Bob は選んだ数を負の数にするという戦略を繰り返します。Bob のターンでは選んだ数の二倍を引くため、前処理で累積和を計算します。

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

void solve() {
    int n, k, x;
    cin >> n >> k >> x;
    vector<int> numbers(n);
    for (auto& num : numbers) cin >> num;
    sort(numbers.begin(), numbers.end(), greater<>());
    long long total_sum = accumulate(numbers.begin(), numbers.end(), 0LL);
    long long best_score = LONG_LONG_MIN;
    long long current_sum = 0;
    for (int i = 0; i < k; ++i) {
        current_sum += numbers[i];
        long long bob_turn = 2 * accumulate(numbers.begin() + i, numbers.begin() + min(n, i + x), 0LL);
        best_score = max(best_score, total_sum - current_sum - bob_turn);
    }
    cout << best_score << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

最初の文字または2番目の文字を削除する

2番目の文字を削除してから最初の文字を削除する操作は、最初の文字を2回削除することと同じです。したがって、最初の k 個の文字について、それぞれのユニークな文字数をカウントします。

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

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    set<char> unique_chars;
    int operations = 0;
    for (char c : s) {
        unique_chars.insert(c);
        operations += unique_chars.size();
    }
    cout << operations << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

クエスト

a の前 n 個の合計と、b の最大値を組み合わせることで最大の結果を求めます。b の最大値を常に更新しながら、a の各要素について計算を行います。

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

void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n), b(n);
    for (auto& x : a) cin >> x;
    for (auto& x : b) cin >> x;
    long long best_result = 0, prefix_sum = 0, max_b = 0;
    for (int i = 0; i < min(n, k); ++i) {
        prefix_sum += a[i];
        max_b = max(max_b, b[i]);
        best_result = max(best_result, prefix_sum + max_b * (k - i - 1));
    }
    cout << best_result << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

交換と削除

文字列内の '0' と '1' の数を数え、それぞれの数がなくなるまで交互に削除します。最後に残った数が削除すべき数となります。

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

void solve() {
    string s;
    cin >> s;
    int count_zero = 0, count_one = 0;
    for (char c : s) {
        if (c == '0') ++count_zero;
        else ++count_one;
    }
    for (char c : s) {
        if (c == '0') {
            if (count_one == 0) break;
            --count_one;
        } else {
            if (count_zero == 0) break;
            --count_zero;
        }
    }
    cout << count_zero + count_one << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

もう一つ壊れたキーボード

'b' と 'B' に対応する文字をスタックに保存し、それぞれの文字が出現した順序を保持します。最後に、スタックから削除された文字を除いた残りの文字列を出力します。

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

void solve() {
    string s;
    cin >> s;
    stack<int> lower_b, upper_B;
    bitset<1000005> marked;
    for (int i = 0; i < s.size(); ++i) {
        if (s[i] == 'b') {
            if (!lower_b.empty()) {
                marked[lower_b.top()] = true;
                lower_b.pop();
            }
        } else if (s[i] == 'B') {
            if (!upper_B.empty()) {
                marked[upper_B.top()] = true;
                upper_B.pop();
            }
        } else if (islower(s[i])) {
            lower_b.push(i);
        } else if (isupper(s[i])) {
            upper_B.push(i);
        }
    }
    for (int i = 0; i < s.size(); ++i) {
        if (!marked[i]) cout << s[i];
    }
    cout << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int test_cases;
    cin >> test_cases;
    while (test_cases--) {
        solve();
    }
    return 0;
}

タグ: C++ アルゴリズム 競技プログラミング codeforces 問題解決

6月13日 22:13 投稿