C++で変更後の配列の最小値が取りうる最大値を求める方法
問題概要
この問題では、サイズnの配列arr[]と数値Sが与えられ、変更後の配列における最小値の最大可能値を求めます。
配列を変更する際には、以下のルールが適用されます。
- 変更前と変更後の配列要素の合計の差は、ちょうどSでなければならない
- 変更後の配列に負の値は許されない
- 変更後の配列の最小値を最大化する必要がある
- 配列の変更は、任意の要素を増加または減少させることによって行える
これらの制約のもとで新しい配列を構成し、その中で最小となる要素の値が最大になるようにします。
例を使って問題を理解しましょう。
入力 : arr[] = {4, 5, 6} S = 2
出力 : 4
説明
この場合、変更後の配列は {4, 5, 5} となり、最小値は4です。これが達成できる最大の最小値となります。
解法アプローチ
変更後の配列の最小値を最大化することが目的です。この問題は、0(最小可能値)からarrmin(最大可能値)までの範囲で、最小値の候補に対して二分探索を行うことで効率的に解けます。各候補値について、制約を満たせるかどうかを判定していきます。
判定条件の考え方
すべての要素をx以上に保つとき、合計を減らせる最大量は「元の配列の合計 − x × n」です。この値がS以上であれば、合計をちょうどSだけ減らしながら全要素をx以上に維持できます。したがって、この条件を満たす最大のxを二分探索で求めればよいのです。
特別なケース
- Sが配列の合計より大きい場合:解は存在しません(-1を返す)
- Sが配列の合計と等しい場合:最小要素の値は0になります
実装例
ソリューションの動作を示すプログラムは以下の通りです。
#include <iostream>
using namespace std;
int findmaximisedMin(int a[], int n, int S){
int minVal = a[0];
int arrSum = a[0];
for (int i = 1; i < n; i++) {
arrSum += a[i];
minVal = min(a[i], minVal);
}
if (arrSum < S)
return -1;
if (arrSum == S)
return 0;
int s = 0;
int e = minVal;
int ans;
while (s <= e) {
int mid = (s + e) / 2;
if (arrSum - (mid * n) >= S) {
ans = mid;
s = mid + 1;
}
else
e = mid - 1;
}
return ans;
}
int main(){
int a[] = { 4, 5, 6 };
int S = 2;
int n = sizeof(a) / sizeof(a[0]);
cout<<"The maximum value of minimum element of the modified array is "<<findmaximisedMin(a, n, S);
return 0;
}
出力
The maximum value of minimum element of the modified array is 4
まとめ
本記事では、配列の要素を増減させて合計の差をSに一致させながら、変更後の配列の最小値を最大化する問題を扱いました。二分探索を用いることで、候補値ごとに線形に確認する方法よりも大幅に効率的に、O(n log(arrmin)) の時間計算量で最適解を求められます。
-
C++の二分探索木(BST)で最小値のノードを見つける方法
二分探索木(Binary Search Tree、BST)が与えられたとき、その木の中から最小の要素を見つけることを考えます。例えば、以下のようなBSTがあるとします。この場合、最小要素は 1 になります。考え方二分探索木の重要な性質として、左部分木には必ず親ノードより小さい値が格納されるというものがあります。この性質を利用すると、次の手順で最小要素を見つけることができます。ルートノードから探索を開始します。現在のノードの左の子が NULL でない間、左の子へ移動を繰り返します。左の子が NULL になったノードの値が、木全体の中で最小の要素です。この操作の計算量は木の高さに依存し、平衡な二分
-
【C++】二分探索木(BST)の最小値を求めるプログラムの解説
本記事では、二分探索木(Binary Search Tree:BST)に格納されたデータの中から最小値を求めるC++プログラムを紹介します。二分探索木には「左の子孫は親より小さい値を持つ」という性質があるため、木の左端にあるノードを順にたどっていくだけで、必ず最小値のノードに到達できます。 アルゴリズム 処理の手順は以下の擬似コードのとおりです。 開始 構造体ndを宣言する。 整数型の変数dを宣言する。 構造体nd型へのポインタlt(左子)を宣言する。 構造体nd型へのポインタrt(右子)を宣言する。 関数new_nd()(戻り値:構造体nd型、引数: