ABC351コンテスト問題解説

問題A: ゲームの点数計算

木青チームと高橋チームの点数をそれぞれ計算し、木青チームの総得点が高橋チームより1点多くなるようにします。

コード例
#include <iostream>
#include <vector>

int main() {
    int score_gq = 0, score_mq = 0;
    int input;

    // 木青チームの9つの点数を入力
    for (int i = 0; i < 9; ++i) {
        std::cin >> input;
        score_gq += input;
    }

    // 高橋チームの8つの点数を入力
    for (int i = 0; i < 8; ++i) {
        std::cin >> input;
        score_mq += input;
    }

    // 木青チームの点数が1点多くなるように調整
    std::cout << score_gq - score_mq + 1 << std::endl;

    return 0;
}

問題B: グリッドの不一致箇所

2つのn×nのグリッドを比較し、最初に不一致となるセルの位置(行と列)を見つけます。

コード例
#include <iostream>
#include <vector>

int main() {
    int n;
    std::cin >> n;

    std::vector grid1(n), grid2(n);

    // 最初のグリッドを読み込む
    for (int i = 0; i < n; ++i) {
        std::cin >> grid1[i];
    }

    // 2つ目のグリッドを読み込み、不一致を探す
    for (int i = 0; i < n; ++i) {
        std::cin >> grid2[i];
        for (int j = 0; j < n; ++j) {
            if (grid1[i][j] != grid2[i][j]) {
                std::cout << i + 1 << " " << j + 1 << std::endl;
                return 0;
            }
        }
    }

    return 0;
}

問題C: 石の合体

与えられた数列を処理します。隣り合う同じ数値があれば、それらを合体させます(値をインクリメントし、一つを削除)。この操作を繰り返し、最終的な数列の長さを出力します。

コード例
#include <iostream>
#include <vector>

int main() {
    int n;
    std::cin >> n;

    std::vector<int> stones;
    int count;

    while (n--) {
        std::cin >> count;
        stones.push_back(count);

        // 合体可能な限りループ
        while (stones.size() > 1 && stones.back() == stones[stones.size() - 2]) {
            stones[stones.size() - 2]++;
            stones.pop_back();
        }
    }

    std::cout << stones.size() << std::endl;
    return 0;
}

問題D: 塗りつぶしと境界

グリッド上の'.'の連結成分をBFSで探索します。各連結成分について、その中のセル数と、'#'に隣接する境界セルの数を数えます。セル数と境界セル数の和が最大のものを求めます。

コード例
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

int main() {
    int rows, cols;
    std::cin >> rows >> cols;

    std::vector grid(rows);
    for (int i = 0; i < rows; ++i) {
        std::cin >> grid[i];
    }

    std::vector visited(rows, std::vector<bool>(cols, false));
    int max_score = 0;
    const int dx[] = {0, 1, -1, 0};
    const int dy[] = {1, 0, 0, -1};

    auto is_boundary = [&](int x, int y) {
        for (int i = 0; i < 4; ++i) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == '#') {
                return true;
            }
        }
        return false;
    };

    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            if (grid[i][j] == '.' && !visited[i][j]) {
                std::queue> q;
                q.push({i, j});
                visited[i][j] = true;
                int cells = 1, boundaries = 0;

                while (!q.empty()) {
                    auto [x, y] = q.front();
                    q.pop();

                    if (is_boundary(x, y)) {
                        boundaries++;
                    }

                    for (int k = 0; k < 4; ++k) {
                        int nx = x + dx[k];
                        int ny = y + dy[k];
                        if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == '.' && !visited[nx][ny]) {
                            visited[nx][ny] = true;
                            q.push({nx, ny});
                            cells++;
                        }
                    }
                }
                max_score = std::max(max_score, cells + boundaries);
            }
        }
    }

    std::cout << max_score << std::endl;
    return 0;
}

問題E: 座標変換と距離和

座標を45度回転し、曼哈頓距離の半分を計算します。座標を(x+y, x-y)に変換することで、4方向への移動は(x±2, y)や(x, y±2)になります。点の偶奇性(変換後のx+yの偶奇)でグループ分けし、各グループ内の点間距離和を前処理和を用いて効率的に計算します。

コード例
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    int n;
    std::cin >> n;

    std::vector<int> x_coords_odd, y_coords_odd, x_coords_even, y_coords_even;

    for (int i = 0; i < n; ++i) {
        int x, y;
        std::cin >> x >> y;
        int transformed_x = x + y;
        int transformed_y = x - y;

        if (transformed_x % 2 == 0) {
            x_coords_even.push_back(transformed_x);
            y_coords_even.push_back(transformed_y);
        } else {
            x_coords_odd.push_back(transformed_x);
            y_coords_odd.push_back(transformed_y);
        }
    }

    // 前処理和を計算するヘルパー関数
    auto calculate_sum = [](std::vector<int>& coords) {
        std::sort(coords.begin(), coords.end());
        long long total_distance = 0;
        long long prefix_sum = 0;
        for (int i = 0; i < coords.size(); ++i) {
            total_distance += (long long)i * coords[i] - prefix_sum;
            prefix_sum += coords[i];
        }
        return total_distance;
    };

    long long answer = calculate_sum(x_coords_odd) + calculate_sum(y_coords_odd) +
                      calculate_sum(x_coords_even) + calculate_sum(y_coords_even);

    std::cout << answer / 2 << std::endl;
    return 0;
}

タグ: AtCoder C++ BFS prefix-sum bit-manipulation

7月14日 02:44 投稿