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

C++で第2党が選挙に勝利するための最小投票数kを求める方法


問題概要

n個の要素を持つ配列Aがあるとします。ある学校にはn人の生徒がおり、それぞれがちょうどk票を持っていて、すべての票を使い切らなければなりません。政党は2つあります。A[i]は、i番目の生徒が第1党にA[i]票を投じたことを表し、その結果、第2党には残りのk − A[i]票が入ります。第2党が選挙に勝てるようにkを設定したい場合、kとして取り得る最小値を求めるのがこの問題です。

例えば、入力が A = [2, 2, 3, 2, 2] の場合、出力は5になります。第1党は 2 + 2 + 3 + 2 + 2 = 11 票を獲得しますが、k = 5 と設定すれば第2党は 3 + 3 + 2 + 3 + 3 = 14 票を獲得でき、選挙に勝利できるからです。

解法のポイント

この問題を効率的に解くために、次の2つの条件に着目します。

  • 下限条件: 各生徒は手持ちのk票の中からA[i]票を第1党に渡すため、kは必ず配列Aの最大値m以上でなければなりません。
  • 勝利条件: 第1党の合計得票数をsとすると、全票数は n × k です。第2党が勝つには n × k − s > s、つまり k > 2s / n が成立する必要があります。

したがって、答えは「m」と「2 × s / n + 1(整数除算)」の大きい方となります。

アルゴリズムの手順

n := 配列Aのサイズ
k を 0 から n 未満まで 1 ずつ増やしながら繰り返す:
    x := A[k]
    m := m と x の最大値
    s := s + x
m と (2 * s / n + 1) の最大値を返す

C++による実装例

それでは、実際のC++コードを見てみましょう。

#include <bits/stdc++.h>
using namespace std;
int solve(vector<int> A){
   int n = A.size(), k = 0, s = 0, m = 0;
   for (int k = 0; k < n; k++){
      int x = A[k];
      m = max(m, x);
      s += x;
   }
   return max(m, 2 * s / n + 1);
}
int main(){
   vector<int> A = { 2, 2, 3, 2, 2 };
   cout << solve(A) << endl;
}

入力

{ 2, 2, 3, 2, 2 }

出力

5

まとめ

このアルゴリズムは配列を一度走査するだけで答えが求まるため、時間計算量はO(n)と非常に効率的です。最大値mと合計sという2つの値から最適なkを直接計算できる点が大きな特徴です。

  1. C++でグラフに長さkを超える単純パスが存在するか判定するアルゴリズム

    概念 重み付きグラフ、グラフ内の始点となる頂点、そして数値k(kは始点から終点までのパスの長さを表します)が与えられたとき、与えられた始点から始まり、任意の他の頂点(終点)で終わる単純パス(閉路を含まないパス)が存在するかどうかを判定することが課題です。以下のグラフを使って説明します。 入力例1 始点 s = 0, k = 64 出力 True この場合、0 -> 7 -> 1 -> 2 -> 8 -> 6 -> 5 -> 3 -> 4 という合計距離68の単純パスが存在し、64を超えているためtrueとなります。 入力例2 始点 s = 0

  2. C++で (x % k) × (x / k) == n を満たす最小の x を求める方法

    2つの正の整数 n と k が与えられたとき、(x % k) × (x / k) が n と等しくなるような正の整数 x を求める必要があります。例えば n = 4、k = 6 の場合、答えは 10 になります。実際に確認すると、(10 % 6) × (10 / 6) = 4 × 1 = 4 となり、条件を満たしています。解法のアプローチここでポイントになるのは、x % k の値が必ず 1 以上 k − 1 以下の範囲に収まるという点です(0 は除外します。x % k が 0 になると積も 0 になり、正の整数 n とは一致しないためです)。そこで、n の約数のうち [1, k − 1] の範