C++で部分配列が山型(マウンテン)かどうかを判定する方法
この記事では、整数配列 arr[] と範囲 [L, R] が与えられたとき、その範囲に含まれる部分配列が山型(マウンテン)の形になっているかどうかを判定する方法を解説します。
「山型」とは、要素が途中まで単調に増加し、頂上から先は単調に減少していく、文字どおり山のような形状を持つ部分配列のことです。
問題の確認
具体例を見てみましょう。
入力 : arr[] = {1, 4, 2, 5, 6, 7, 3, 0}, 範囲 = [2, 7]
出力 : Yes説明:
範囲 [2, 7] の部分配列 = {2, 5, 6, 7, 3, 0}
値がまず増加し、その後減少しているため山型です。解法アプローチ
この問題に対するシンプルな解決策は、補助配列を活用する方法です。あらかじめ前処理を行っておくことで、任意の範囲について高速に判定できるようになります。
具体的な手順は次のとおりです。
- left[i]:位置 i まで見たときに、増加が続く最後のインデックスを記録します。
- right[i]:位置 i 以降で、値の減少が始まる最初のインデックスを記録します。
- 範囲 [L, R] が山型であるかどうかは、「right[L] >= left[R]」という条件式ひとつで判定できます。
前処理には O(N)、範囲ごとの判定には O(1) しかかからないため、複数の範囲に対して繰り返し問い合わせる場合にも非常に効率的です。
C++での実装例
上記の解法を実装したプログラムがこちらです。
#include <iostream>
using namespace std;
int processArray(int arr[], int N, int left[], int right[]){
left[0] = 0;
int increasingValR = 0;
for (int i = 1; i < N; i++){
if (arr[i] > arr[i - 1])
increasingValR = i;
left[i] = increasingValR;
}
right[N - 1] = N - 1;
int decreasingValL = N - 1;
for (int i = N - 2; i >= 0; i--){
if (arr[i] > arr[i + 1])
decreasingValL = i;
right[i] = decreasingValL;
}
}
bool isMountainSubArray(int arr[], int left[], int right[], int L, int R){
return (right[L] >= left[R]);
}
int main(){
int arr[] = {2, 3, 2, 4, 4, 6, 3, 2};
int N = sizeof(arr) / sizeof(int);
int left[N], right[N];
processArray(arr, N, left, right);
int L = 0;
int R = 2;
if (isMountainSubArray(arr, left, right, L, R))
cout<<"The subarray is in mountain form";
else
cout<<"The subarray is not in mountain form";
return 0;
}出力結果
The subarray is in mountain form
コードの解説
このプログラムでは、配列 {2, 3, 2, 4, 4, 6, 3, 2} の範囲 [0, 2]、つまり部分配列 {2, 3, 2} を対象に判定を行っています。「2 → 3」と増加した後「3 → 2」と減少しているため、これは山型であり、正しく判定できていることがわかります。
processArray() 関数が前処理として left 配列と right 配列を構築し、isMountainSubArray() 関数が条件式によって実際の判定を行います。判定自体は比較一回で完了するため、大量の範囲クエリを扱うケースでも高いパフォーマンスを発揮するアプローチです。
-
C++で厳密に増加する部分配列の最大和を求めるアルゴリズム
問題の概要n 個の整数からなる配列が与えられたとき、その中に存在する「厳密に増加する(strictly increasing)部分配列」の中で、要素の合計が最大となるものを求めます。例として、次のような配列を考えてみましょう。[1, 2, 3, 2, 5, 1, 7]この配列には、厳密に増加している部分配列が3つ存在します。{1, 2, 3}{2, 5}{1, 7}それぞれの合計は 6、7、8 となり、この中で最大となるのは {1, 7} の合計 8 です。解き方の考え方この問題は、現在の部分配列の合計(current_sum)とこれまでの最大合計(max_sum)を追跡しながら配列を一度だけ
-
【C++】2つの特定のノード間にパスが存在するかどうかを確認するプログラム
グラフにおいて「ある頂点から別の頂点へ到達できるか」を判定する問題は、経路探索やネットワーク解析など、さまざまな場面で登場する基本的な課題です。本記事では、C++を用いて、指定した2つのノード(頂点)間にパス(経路)が存在するかどうかを確認するプログラムを紹介します。 アルゴリズム このプログラムでは、幅優先探索(BFS)をベースとした関数 isReach() を使い、始点 s から終点 d への到達可能性を調べます。処理の手順は以下の通りです。 開始 isReach() は、d が s から到達可能かどうかを判定する再帰的な関数である: A) すべての頂点を「未訪問」としてマーク