C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で数学コンテストの勝者を判定するアルゴリズムの解説


問題概要

サイズ n の2つの配列 P と T、および定数 c が与えられます。Amal と Bimal の2人が数学コンテストに参加することになりました。コンテストには n 問の問題が出題され、i 番目の問題の初期スコアは P[i]、それを解くのにかかる時間は T[i] です。P と T はどちらも昇順にソートされています。c は減点を決める定数で、ある問題をコンテスト開始 x 分後に提出した場合、その問題から得られるスコアは max(0, P[i] - c * x) となります。

Amal は問題を 1, 2, ..., n の順番で解き、Bimal は n, n-1, ..., 1 の順番で解きます。それぞれが自分の順序ですべての問題に取り組んだとき、合計スコアが高い方を勝者として求めてください。両者のスコアが同じ場合は「Tie」(引き分け)と出力します。

たとえば、入力が c = 2、P = [50, 85, 250]、T = [10, 15, 25] のとき、出力は「Amal」となります。

アルゴリズムの手順

この問題は、次の手順で解くことができます。

n := P のサイズ
m := 0
ans1 := 0
ans2 := 0
i := 0 として初期化し、i < n の間、i を 1 ずつ増やしながら繰り返す:
    m := m + T[i]
    ans1 := ans1 + max(0, P[i] - c * m)
m := 0
i := n - 1 として初期化し、i >= 0 の間、i を 1 ずつ減らしながら繰り返す:
    m := m + T[i]
    ans2 := ans2 + max(0, P[i] - c * m)
もし ans1 > ans2 ならば:
    "Amal" を返す
そうでなく ans1 < ans2 ならば:
    "Bimal" を返す
それ以外の場合:
    "Tie" を返す

考え方のポイント

各参加者のスコアは、配列を順方向(または逆方向)に一度だけ走査し、そこまでに費やした時間の累積値 m を更新しながら、各問題について max(0, P[i] - c * m) を加算することで求められます。後から解く問題ほど減点の影響が大きくなるため、この累積時間の管理が重要です。計算量は配列を1回ずつ見るだけの O(n) で、非常に効率的です。

C++による実装例

理解を深めるために、実際の実装例を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
string solve(int c, vector<int> P, vector<int> T){
    int n = P.size();
    int m = 0, ans1 = 0, ans2 = 0;
    for (int i = 0; i < n; i++){
        m += T[i];
        ans1 += max(0, P[i] - c * m);
    }
    m = 0;
    for (int i = n - 1; i >= 0; i--){
        m += T[i];
        ans2 += max(0, P[i] - c * m);
    }
    if (ans1 > ans2)
        return "Amal";
    else if (ans1 < ans2)
        return "Bimal";
    else
        return "Tie";
}
int main(){
    int c = 2;
    vector<int> P = { 50, 85, 250 };
    vector<int> T = { 10, 15, 25 };
    cout << solve(c, P, T) << endl;
}

入力

2, { 50, 85, 250 }, { 10, 15, 25 }

出力

Amal

実行結果の解説

この入力では、Amal の合計スコアは 215 点、Bimal の合計スコアは 205 点となります。したがって、より高いスコアを獲得した「Amal」が出力されます。

  1. C++で解くマス塗り分けゲームの勝者判定プログラム

    問題の概要 要素数Nの2つの配列AとBがあるとします。Amal(アマル)とBimal(ビマル)の2人が、1からNまでの番号が振られたマスを持つ盤面でゲームを行います。盤面にはN-1本の道があり、i番目の道はマスA[i]とマスB[i]を結んでいます。どのマスからでも、隣接するマスへの移動を繰り返すことで他のすべてのマスに到達できます。つまり、この盤面は木構造になっています。 初期状態では、マス1が黒色、マスNが白色に塗られており、それ以外のマスは無色です。Amalが先手となり、2人は交互に手番を進めます。Amalは黒いマスに隣接する無色のマスを選んで黒く塗り、Bimalは白いマスに隣接する無色

  2. 【C++】'D'と'I'の署名から辞書順最小の順列を見つけるアルゴリズム

    問題概要D と I の2種類の文字だけで構成された「秘密の署名」を考えます。D は隣接する2つの数の間の減少関係を、I は増加関係を表します。この署名は、1 から n までの相異なる整数をすべて含む特別な整数配列から構成されます。たとえば、署名 DI は [2, 1, 3] や [3, 1, 2] のような配列から構成できます。一方、[3, 2, 4] や [2, 1, 3, 4] のような配列では構成できず、不正な構成となります。ここでの課題は、入力として与えられた署名に一致する [1, 2, ..., n] の順列のうち、辞書順で最小のものを見つけることです。入力が DI の場合、出力は