C++で解くキャンディ分配問題:隣接する子供の評価値に基づく最小キャンディ数の求め方
問題概要
N人の子供が一列に並んでおり、それぞれの子供には評価値(rating)が割り当てられています。これらの子供たちにキャンディを配る際、以下の条件を満たす必要があります。
すべての子供は最低1個のキャンディを受け取る必要があります。
評価値が高い子供は、隣にいる子供よりも多くのキャンディを受け取る必要があります。
このとき、配るべきキャンディの最小個数を求めます。
例えば、入力が [1, 1, 3] の場合、出力は 4 となります。このとき、子供たちはそれぞれ 1個、1個、2個 のキャンディを受け取ることになります。
解法のアプローチ
この問題は、左から右、右から左の2回の走査(2パス方式)で効率的に解くことができます。手順は以下の通りです。
配列 ratings のサイズを n とし、サイズ n の配列 dp を作成して、すべての要素を 1 で初期化します(まず全員に1個ずつ配るため)。
変数 ret を 0 で初期化します。
左から右へ走査します(i を 1 から n - 1 まで)。
ratings[i] > ratings[i - 1] の場合、dp[i] := dp[i - 1] + 1 とします。
右から左へ走査します(i を n - 2 から 0 まで)。
ratings[i] > ratings[i + 1] の場合、dp[i] := max(dp[i], dp[i + 1] + 1) とします。
ret := dp の全要素の合計とします。
ret を返します。
C++による実装例
以下の実装例を見て、理解を深めましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector <int> dp(n, 1);
int ret = 0;
for(int i = 1; i < n; i++){
if(ratings[i] > ratings[i - 1]){
dp[i] = dp[i - 1] + 1;
}
}
for(int i = n - 2; i >= 0; i--){
if(ratings[i] > ratings[i + 1]){
dp[i] = max(dp[i], dp[i + 1] + 1);
}
}
for(int i = 0; i < n; i+=1){
ret += dp[i];
}
return ret;
}
};
main(){
Solution ob;
vector<int> v = {1,1,3};
cout << (ob.candy(v));
}
入力
[1,1,3]
出力
4
計算量
このアルゴリズムの時間計算量は O(n)、空間計算量は O(n) です。配列を2回走査するだけで済むため、非常に効率的な解法となっています。
-
C++で解くリスのナッツ収集シミュレーション ― 最小移動距離を求めるアルゴリズム
問題概要 1本の木、1匹のリス、そして複数のナッツがフィールド上にあります。それぞれの位置は2次元グリッドのセルで表現されます。この問題の目的は、リスがすべてのナッツを集めて木の下に1個ずつ運ぶときの最小移動距離を求めることです。 リスの行動には次の制約があります。 一度に持てるナッツは最大1個 移動は上下左右の4方向で、隣接するセルへのみ可能 距離は移動回数(ステップ数)で表される たとえば、入力が「高さ: 5 / 幅: 7 / 木の位置: [2,2] / リスの位置: [4,4] / ナッツ: [[3,0], [2,5]]」の場合、出力は 12 となります。 解法のポイント まず、
-
C++で解く長方形エリアII ― 座標圧縮と走査線法による被覆面積の計算
問題概要 軸に平行な長方形のリストが与えられるものとします。各 rectangle[i] = {x1, y1, x2, y2} において、(x1, y1) は i 番目の長方形の左下隅の座標、(x2, y2) は右上隅の座標を表します。 求めたいのは、平面上でこれらすべての長方形が覆っている領域の合計面積です。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返すことになっています。 たとえば、入力が次のような場合を考えてみましょう。 このとき、出力は 6 となります。 解法の方針:座標圧縮+走査線(スイープライン) この問題は、座標圧縮(座標の離散化)と走査線法(