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を直接計算できる点が大きな特徴です。
-
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
-
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] の範