AtCoder Beginner Contest 363 解説:アルゴリズム設計と実装

問題A:数値間隔の算出

入力値 $R$ が属する百の位区間を特定し、その区間の上限値との差分を出力する問題です。条件分岐を用いる代わりに、整数除算による切り上げ演算の特性を活用することで、簡潔な式に変換可能です。

#include <iostream>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int current_rating;
    if (!(cin >> current_rating)) return 0;
    
    int next_hundred = ((current_rating + 99) / 100) * 100;
    cout << (next_hundred - current_rating) << '\n';
    return 0;
}

問題B:順位ベースの閾値評価

与えられた数列を昇順に整列させ、下から $P$ 番目の要素を取得します。取得した値が目標スコア $T$ に満たない場合、その不足分を計算し、既に達成している場合は $0$ を返します。インデックスのオフセット計算に注意が必要です。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, target_val, rank;
    cin >> n >> target_val >> rank;
    
    vector<int> data(n);
    for (int &x : data) cin >> x;
    
    sort(data.begin(), data.end());
    
    int reference = data[n - rank];
    cout << max(0, target_val - reference) << '\n';
    return 0;
}

問題C:順列生成と部分回文の除外

文字列の全順列を生成し、各配置に対して長さ $K$ の連続部分列が回文構造を持つかを検証します。`next_permutation` による辞書順走査を行い、スライディングウィンドウ形式で中央一致判定を実行します。回文が存在しないパターン数を累積します。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

bool contains_palindrome(const string& s, int len) {
    for (size_t i = 0; i <= s.size() - len; ++i) {
        bool match = true;
        for (int offset = 0; offset < len / 2; ++offset) {
            if (s[i + offset] != s[i + len - 1 - offset]) {
                match = false;
                break;
            }
        }
        if (match) return true;
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, k;
    cin >> n >> k;
    string base;
    cin >> base;
    
    sort(base.begin(), base.end());
    int valid_count = 0;
    
    do {
        if (!contains_palindrome(base, k)) {
            ++valid_count;
        }
    } while (next_permutation(base.begin(), base.end()));
    
    cout << valid_count << '\n';
    return 0;
}

問題D:構成ベースの回文数探索

回文数は桁数に応じて規則的に増加します(例:1桁9個、2桁9個、3桁90個…)。対象の $N$ がどの桁数群に属するかを算出し、前半分の桁を数値生成します。生成した前半を鏡像化して結合する際、奇数桁か偶数桁かに応じて中央文字の重複を調整します。$N=1$ の場合は $0$ を特別扱いします。

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    long long n;
    cin >> n;
    if (n == 1) {
        cout << "0\n";
        return 0;
    }
    --n;
    
    long long block_size = 9;
    while (n >= block_size * 2) {
        n -= block_size * 2;
        block_size *= 10;
    }
    --n;
    
    string prefix = to_string(n % block_size + block_size / 9);
    string suffix = prefix;
    reverse(suffix.begin(), suffix.end());
    
    if (n < block_size) {
        prefix.pop_back();
    }
    cout << prefix + suffix << '\n';
    return 0;
}

問題E:水位上昇シミュレーション

グリッド内の高地が海面上昇に伴い侵食される過程をシミュレーションします。初期状態で境界に接するセルを最小ヒープに投入し、毎年の水位上昇時にヒープの最小値と比較します。侵食されたセルの隣接マスを未訪問セットから除外し、ヒープに追加することで洪水伝播を効率的に処理します。

#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int h, w, max_year;
    cin >> h >> w >> max_year;
    
    vector<vector<int>> terrain(h + 2, vector<int>(w + 2));
    priority_queue<pair<int, pair<int, int>>,
                   vector<pair<int, pair<int, int>>>,
                   greater<>> pq;
    set<pair<int, int>> inland;
    
    for (int r = 1; r <= h; ++r) {
        for (int c = 1; c <= w; ++c) {
            cin >> terrain[r][c];
            bool is_edge = (r == 1 || r == h || c == 1 || c == w);
            if (is_edge) {
                pq.push({terrain[r][c], {r, c}});
            } else {
                inland.insert({r, c});
            }
        }
    }
    
    const int dr[] = {0, 0, 1, -1};
    const int dc[] = {1, -1, 0, 0};
    int remaining = h * w;
    
    for (int year = 1; year <= max_year; ++year) {
        while (!pq.empty()) {
            auto [height, pos] = pq.top();
            auto [cur_r, cur_c] = pos;
            if (height > year) break;
            pq.pop();
            --remaining;
            
            for (int k = 0; k < 4; ++k) {
                int nr = cur_r + dr[k];
                int nc = cur_c + dc[k];
                if (nr >= 1 && nr <= h && nc >= 1 && nc <= w) {
                    if (inland.erase({nr, nc})) {
                        pq.push({terrain[nr][nc], {nr, nc}});
                    }
                }
            }
        }
        cout << remaining << '\n';
    }
    return 0;
}

タグ: AtCoder competitive-programming C++ priority-queue combinatorics

5月14日 00:46 投稿