C++で階段の段数を求める方法|二分探索によるO(log N)の効率的な解法
問題概要
この問題では、階段の建設に使えるレンガの数を表す整数 N が与えられ、そのレンガで何段の階段を作れるかを求めます。
階段は与えられたレンガを使って下から順に組み上げていきます。各段は直前の段より1個多くのレンガを必要とし、最初の段は2個のレンガで作られます。つまり、1段目=2個、2段目=3個、3段目=4個という具合です。残りのレンガが次の段を積むのに足りなくなった時点で構築を終え、それまでに完成した段数が答えとなります。
入出力例
入力
N = 40
出力
7
動作の解説
N = 40 のとき、段を積むごとにレンガの消費数と残数は次のように変化します。
| 段 | 必要レンガ数 | 累計使用数 | 残りレンガ数 |
|---|---|---|---|
| 1 | 2 | 2 | 38 |
| 2 | 3 | 5 | 35 |
| 3 | 4 | 9 | 31 |
| 4 | 5 | 14 | 26 |
| 5 | 6 | 20 | 20 |
| 6 | 7 | 27 | 13 |
| 7 | 8 | 35 | 5 |
8段目を作るには9個のレンガが必要ですが、残りは5個しかありません。したがって、これ以上段を積むことはできず、答えは7段となります。
解法アプローチ
1. ループによるシンプルな解法
最も素直な解法は、必要レンガ数を2から始めて1個ずつ増やしながらループを回し、累計使用数がNを超えた時点で停止して、その直前の段数を返す方法です。
この手法はシンプルで分かりやすい一方、段数に比例して繰り返しが増えるため、時間計算量は O(N) となります。
2. 総和公式と二分探索による高速な解法
k段の階段に必要なレンガの総数は、等差数列の和の公式から次のように表せます。
2 + 3 + … + (k + 1) = k(k + 3) / 2
この累積必要数はkの増加に対して単調に増えていくため、「k(k + 3) / 2 ≤ N」を満たす最大のkを二分探索で効率よく絞り込めます。実装では探索を整理するため T = 2N と置き、「m × (m + 1) ≤ T」となる最大のmを求めたうえで答えを導出しています。これにより、時間計算量を O(log N) まで抑えられます。
C++での実装例
以下は、二分探索を用いた解法の動作を示すプログラムです。
#include <iostream>
using namespace std;
int findStairCount(int T){
int low = 1;
int high = T/2;
while (low <= high) {
int mid = (low + high) / 2;
if ((mid * (mid + 1)) == T)
return mid;
if (mid > 0 && (mid * (mid + 1)) > T && (mid * (mid - 1)) <= T)
return mid - 1;
if ((mid * (mid + 1)) > T)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
int main(){
int N = 60;
int stepCount = findStairCount(2*N);
if (stepCount != -1)
stepCount--;
cout<<"作成できる階段の段数:"<<stepCount;
return 0;
}
出力
作成できる階段の段数:9
この例では N = 60 が与えられており、2 + 3 + … + 10 = 54 となるので9段まで積めますが、10段目に必要な11個のレンガは残っていないため、答えは9段となります。
計算量のまとめ
・時間計算量:O(log N)(二分探索の反復回数に依存)
・空間計算量:O(1)(追加のメモリは不要)
-
C++で列車の停車駅の組み合わせ数を求める方法
地点XとYの間にはn個の中間駅があるとします。ここで、「どの2つの停車駅も隣り合わない」という条件のもとで、s個の駅に停車する列車の配置方法が何通りあるかを求める問題を考えてみましょう。この記事では、停車駅の組み合わせ数を求めるためのアプローチを段階的に詳しく解説します。この問題は、本質的には組合せ論の問題であり、s個の停車駅の選び方の総数を求めることになります。 問題を解くアプローチ まず具体例として、中間駅が8個あり、そのうち3個の駅に停車させたい場合を考えてみます。 n = 8, s = 3 このとき、列車が停車できない駅は(n − s)、つまり5個残ることになります。 停車できない
-
C++で集合の反射関係の数を求める方法
この記事では、C++を使って集合上に定義できる反射関係(reflexive relation)の総数を求める方法について解説します。問題設定としては、整数 n が与えられたとき、n 個の自然数からなる集合上に存在する反射関係の個数を求めるというものです。 反射関係とは 集合 A 上の関係 R が反射的であるとは、「A に属するすべての要素 a に対して、順序対 (a, a) が必ず R に含まれる」という条件を満たすことを意味します。数式で表すと次のようになります。 (a, a) ∈ R (∀ a ∈ A) 具体的な入出力の例を見てみましょう。 入力 : x = 1 出力 : 1 説明 : 集