C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でステッピングナンバー(Stepping Numbers)を効率的に求める方法


2つの整数 lowhigh が与えられたとき、範囲 [low, high] に含まれるすべての「ステッピングナンバー(Stepping Number)」を昇順に並べたリストを求めます。ステッピングナンバーとは、隣り合うどの桁同士も絶対差がちょうど1になる整数のことです。たとえば 321 はステッピングナンバーですが、421 は該当しません。入力が low = 0、high = 21 の場合、出力は [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 21] となります。

解き方のアプローチ

この問題は、DFS(深さ優先探索)によって条件を満たす数を段階的に生成するのが効果的です。手順は以下のとおりです。

  • 結果を一時的に保持する配列 temp を用意する
  • solve() メソッドを作成する。引数は highseedlenlen の初期値は0)
  • seedhigh を超えたら再帰を終了して戻る
  • seedtemp 配列に追加する
  • seed が 0 の場合:
    • 1〜9 の各 i に対して solve(high, i, 1) を呼び出す
  • それ以外の場合:
    • lastDigit = seed mod 10 を計算する
    • lastDigit >= 1 かつ len + 1 <= 10 ならば、solve(high, (seed*10) + lastDigit − 1, len + 1) を呼び出す
    • lastDigit <= 8 かつ len + 1 <= 10 ならば、solve(high, (seed*10) + lastDigit + 1, len + 1) を呼び出す
  • メイン処理では以下を実行する:
    • solve(high, 0, 0) を呼び出す
    • temp 配列をソートする
    • 答え用の配列 ans を作成する
    • i を 0 から temp のサイズ−1 までループし、temp[i] >= low ならば temp[i]ans に追加する
    • ans を返す

なぜこの方法が有効なのか

範囲内のすべての整数を1つずつチェックすると非効率ですが、この手法では「次の桁は現在の末尾の桁±1である」という性質を利用し、ステッピングナンバーになり得る数だけを木構造的に生成できます。また、桁数を10桁までに制限しているのは、大きな数によるオーバーフローを防ぐためです。生成後にソートして下限値 low 以上の要素だけを取り出せば、目的のソート済みリストが得られます。

C++での実装例

以下のコードで実際の動作を確認してみましょう。

#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
    cout << "[";
    for(int i = 0; i<v.size(); i++){
        cout << v[i] << ", ";
    }
    cout << "]"<<endl;
}
typedef long long int lli;
class Solution {
public:
    vector <lli> temp;
    void solve(int high,lli seed=0, int len =0){
        if(seed>high){
            return;
        }
        temp.push_back(seed);
        if(!seed){
            for(int i =1;i<=9;i++){
                solve(high,i,1);
            }
        } else {
            int lastDigit = seed%10;
            if(lastDigit>=1 && len+1<=10)
                solve(high, (seed*10) + lastDigit-1,len+1);
            if(lastDigit<=8 && len+1<=10)
                solve(high, (seed*10) + lastDigit+1,len+1);
        }
    }
    vector<int> countSteppingNumbers(int low, int high) {
        solve(high);
        sort(temp.begin(),temp.end());
        vector <int> ans;
        for(int i =0;i<temp.size();i++){
            if(temp[i]>=low)ans.push_back(temp[i]);
        }
        return ans;
    }
};
main(){
    Solution ob;
    print_vector(ob.countSteppingNumbers(0,40));
}

入力

0
40

出力

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 21, 23, 32, 34]

  1. 【C++入門】エマープ数(Emirp)とは?n以下のエマープ数をすべて出力するプログラム

    エマープ数(Emirp number)とは、素数の一種で、その桁を逆順に並べ替えると別の素数になる数のことです。ここでいう「別の素数」とは、元の数と同じ値にならないものを指します。 Emirpは「prime(素数)」を逆から読んだ言葉 すべての素数がエマープ数になるわけではありません。たとえば、回文素数(121のように逆から読んでも同じ数になる素数)や、1桁の素数(2、3、5、7)は、桁を逆にしても同じ数または意味を持たないため、エマープ数には含まれません。 エマープ数の例:13、17、37、733 などがあります。 13 を逆にすると 31(素数)→ エマープ数 17 を逆にすると 71

  2. C++でデューデニー数(Dudeney Number)を判定する方法

    デューデニー数とは? デューデニー数(Dudeney Number)とは、数論で定義される特殊な自然数の一つです。「ある自然数が、別の自然数の完全立方数に等しく、かつ元の数の各桁の数字和が、その立方根となる数の桁和と一致する」とき、その数をデューデニー数と呼びます(Wikipediaより)。 この数は、イギリスの著名なパズル作家であるヘンリー・デューデニー(Henry Dudeney)によって発見されました。数学的には次の式で表されます。 有名な例としては 512 = 8³ が挙げられます。512 の桁和は 5 + 1 + 2 = 8 となり、立方根である 8 と一致するため、512 はデ