【C++】d日以内に最初の山へ集められる干し草の俵の最大数を求めるアルゴリズム
問題概要
n個の要素を持つ配列Aと値dが与えられます。ある農家では、n個の干し草の山が一列に並べられており、i番目の山にはA[i]個の干し草が積まれています。
ここに1頭の牛がいて、1日に1回だけ、どれかの山から隣接する山へ干し草を1つ移動させることができます(何もしない日があっても構いません)。牛はd日間で1番目の山の干し草の数をできるだけ多くしたいと考えています。d日後の1番目の山にある干し草の最大数を求めてください。
例えば、入力が d = 5、A = [1, 0, 3, 2] の場合、出力は 3 になります。これは次のような手順で移動できるためです。
- 1日目:3番目の山 → 2番目の山へ1つ移動
- 2日目:3番目の山 → 2番目の山へさらに1つ移動
- 3日目:2番目の山 → 1番目の山へ1つ移動
- 4日目:2番目の山 → 1番目の山へさらに1つ移動
こうして、1番目の山は 1 + 2 = 3 個の干し草を持つことになります。
解法のアプローチ
この問題のポイントは、i番目の山から1番目の山へ干し草を1つ運ぶのにi回の移動(つまりi日)が必要だという点です。なぜなら、牛は隣接する山にしか移動できないため、距離iだけ離れた山からの輸送にはi日かかるからです。
したがって、残り日数dの中でi番目の山から運べる干し草の数は、以下のように表せます。
- w = min(A[i], d / i)
つまり「その山に元々ある干し草の数」と「残り日数で運べる上限」の小さい方を採用し、それをa0に加算しながら、使用した日数分(w × i)をdから差し引いていきます。これをすべての山に対して繰り返すことで答えが得られます。
アルゴリズムの手順
a0 := A[0]
n := size of A
for initialize i := 1, when i < n, update (increase i by 1), do:
ai := A[i]
w := minimum of ai and d / i
a0 := a0 + w
d := d - w * i
return a0
C++での実装例
以下に実際のC++コードを示します。
#include <bits/stdc++.h>
using namespace std;
int solve(int d, vector<int> A){
int a0 = A[0];
int n = A.size();
for (int i = 1; i < n; i++){
int ai = A[i];
int w = min(ai, d / i);
a0 += w;
d -= w * i;
}
return a0;
}
int main(){
int d = 5;
vector<int> A = { 1, 0, 3, 2 };
cout << solve(d, A) << endl;
}
入力
5, { 1, 0, 3, 2 }
出力
3
計算量について
このアルゴリズムは配列を一度走査するだけで済むため、時間計算量はO(n)、空間計算量はO(1)と非常に効率的です。貪欲法(Greedy法)の典型的な応用例であり、「遠い山ほどコストが高い」という性質を利用して、各山から独立に最適値を決定しています。
-
C++で1人目の学生に割り当て可能な最大スコアを求める方法
n 個の要素を持つ配列 A と数値 m が与えられているとします。n 人の学生が試験を受けており、取りうる最高得点は m です。A[i] は i 番目の学生の得点を表します。各学生の得点は自由に変更できますが、次の条件を満たす必要があります。 どの得点も m を超えないこと すべての得点が整数であること 全学生の平均点が変化しないこと このとき、1 人目の学生の得点を最大化したい場合、割り当てられる最高得点はいくつになるでしょうか。 例 たとえば、入力が A = [1, 2, 3, 4]、m = 10 の場合を考えてみましょう。このときの出力は 10 になります。元の平均点は 2.5 です
-
C++で異なる要素の数が最大となる部分列の個数を求める方法
整数のみを含む配列 arr[] が与えられます。この問題の目的は、「異なる要素(ユニークな要素)の数が最大になる」ような配列 arr[] の部分列が全部で何通りあるかを求めることです。例えば、配列が [4, 1, 2, 3, 4] の場合、条件を満たす部分列は [4, 1, 2, 3] と [1, 2, 3, 4] の2つになります。入力例と出力例で理解する入力 − arr[] = { 1, 3, 5, 4, 2, 3, 1 }出力 − 異なる要素が最大となる部分列の個数 − 4説明 − 異なる要素は 1、2、3、4、5 の5種類です。条件を満たす部分列は次の4通りになります。[ 1, 3,