C++で数直線上の目標位置に到達するための最小移動回数を求める
無限に続く数直線上の位置0に立っているとします。目標地点は位置 target にあります。各移動では、左方向または右方向のどちらにも進むことができ、n回目の移動(1から開始)ではちょうどn歩進むことになります。このとき、目的地に到達するために必要な最小の移動回数を求めるのが課題です。
例えば、target = 3 の場合、答えは 2回 となります。0から1へ(+1)、1から3へ(+2)と進むことで、ちょうど2回の移動で目標に到達できます。
解法のアプローチ
この問題は、以下の手順で効率的に解くことができます。
target := |target|、cnt := 0と初期化するtarget > 0の間、以下を繰り返す:cntを1増やすtarget := target − cnt
- ループ終了後、
target(= 超過分)が偶数ならcntを返し、そうでなければcnt + 1 + (cnt mod 2)を返す
なぜこの方法でうまくいくのか
まず、1 + 2 + 3 + … と移動距離を加算していき、合計が初めて target 以上になった時点での回数を cnt とします。このときの超過分(target を引いた残り)が偶数であれば、途中のいくつかの移動の向きを反転させることで、総和を変えずにちょうど target に到達できます。
一方、超過分が奇数の場合は、向きの反転だけでは偶奇が変わらないため、追加の移動が必要になります。cnt が偶数なら1回、奇数なら2回の追加移動で偶奇が反転し、必ず到達できることが証明されています。式 cnt + 1 + cnt % 2 はこの調整をまとめて表したものです。
C++による実装例
それでは、理解を深めるために実際の実装を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
int reachNumber(int target) {
target = abs(target);
int cnt = 0;
while(target > 0){
target -= ++cnt;
}
return target % 2 == 0? cnt : cnt + 1 + cnt % 2;
}
};
main(){
Solution ob;
cout << (ob.reachNumber(3));
}
入力
3
出力
2
このアルゴリズムの計算量は O(√target) であり、1からcntまでの和が target を超えるまでループするだけなので、非常に効率的に動作します。負の target も絶対値を取ることで対称的に処理できる点もポイントです。
-
C++で質素数(Frugal Number)を判定する方法【サンプルコード付き】
この記事では、正の整数 N が与えられたときに、その数が質素数(Frugal Number)であるかどうかを判定するプログラムを C++ で作成する方法を解説します。 質素数とは? 質素数(FRUGAL NUMBER)とは、その数自身の桁数が、素因数分解による表現の桁数よりも厳密に大きい数のことです。 例:625 の場合 625 を素因数分解すると 54 となります。 625 自身の桁数:3 桁 54 の表現の桁数:2 桁 3 は 2 よりも厳密に大きいため、625 は質素数です。 最初のいくつかの質素数:125、128、243、256、343、512、625 など 問題を理解するための具
-
C++で五胞体数(ペンタトープ数)を求める方法
五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の