-
C++で完全グラフから求める辺素な全域木の最大数
完全グラフが与えられたとき、そのグラフから構成できる辺素な全域木(Edge Disjoint Spanning Tree)の数を求める方法を解説します。辺素な全域木とは、集合に含まれるどの2つの木も互いに共通の辺を1本も持たない全域木のことです。例えば、頂点数Nが4の場合、答えは2になります。4つの頂点を持つ完全グラフは以下のようになります。このグラフから構成できる2つの辺素な全域木は以下の通りです。辺素な全域木の最大数の求め方N個の頂点を持つ完全グラフから構成できる辺素な全域木の最大数は、次の式で求められます。⌊n/2⌋この式が成り立つ理由は以下の通りです。完全グラフの辺の総数は n(n-1
-
【C++】分割統治法で最大部分配列和を求める方法を解説
正の値と負の値が混在する数列が与えられたとき、その中から「要素が連続する部分配列(サブアレイ)」のうち合計が最大になるものを求める問題を考えます。例えば、数列 {-2, -5, 6, -2, -3, 1, 5, -6} の場合、最大部分配列和は 7 となり、これは {6, -2, -3, 1, 5} の合計に相当します。この問題は、分割統治法(Divide and Conquer)を用いることで効率的に解くことができます。アルゴリズムの手順配列を中央で2つに分割する以下の3つの値のうち最大のものを求める左側の部分配列における最大部分配列和右側の部分配列における最大部分配列和中央をまたいで(左右
-
C++でカウントソートを使って中央値と最頻値を求める方法
サイズnの配列が与えられたとき、カウントソートの手法を応用して中央値(メジアン)と最頻値(モード)を求めることを考えます。この手法は、配列の要素が限られた範囲内にある場合に特に有効です。例えば、要素が{1, 1, 1, 2, 7, 1}である配列の場合、最頻値は1、中央値は1.5となります。 中央値と最頻値とは 中央値(メジアン):数値を昇順に並べたリストの中央に位置する値 最頻値(モード):リスト内で最も多く出現する要素 求め方の手順 中央値と最頻値を求めるには、以下の手順に従います。 入力配列のサイズをnと仮定します。 各値の出現回数を記録するカウント配列を作成します。 カウント配列
-
C++で特定の文字をすべて削除した後のASCII値の合計を最小化する方法
文字列が与えられたとき、特定の1文字の出現をすべて削除した後の、各文字のASCII値の合計を最小化することを考えます。例として、「hello」という文字列を扱ってみましょう。この文字列のASCII値の合計は (104 + 101 + 108 + 108 + 111) = 532 となります。次に、各文字の出現回数を確認します。h は1回出現するため、コストは 1 × 104 = 104e は1回出現するため、コストは 1 × 101 = 101l は2回出現するため、コストは 2 × 108 = 216o は1回出現するため、コストは 1 × 111 = 111この中で最も多く出現しているのは
-
C++で2つの数値文字列を同一にするための最小コストの求め方
問題の概要 2つの数値文字列 A と B が与えられたとき、両者を同一の文字列に揃えるために必要な最小コストを求めます。実行できる操作は「文字列から数字を1つ削除する」ことだけで、削除にかかるコストはその数字の値そのものになります。 例えば、A = "6789"、B = "7859" という2つの文字列の場合、A から「6」を、B から「5」をそれぞれ削除すれば2つの文字列が一致します。このとき必要なコストは 5 + 6 = 11 です。 解法のアプローチ:最長共通部分列(LCS)の応用 この問題は、古典的な最長共通部分列(LCS:Longest Com
-
C++で2つの文字列を同一にするための最小コスト
2つの文字列 A と B、そしてそれぞれのコスト値 CostA と CostB が与えられているとします。このとき、A と B を同一にするために必要な最小コストを求めるのが本問題です。文字列からは自由に文字を削除でき、文字列 A から1文字削除するたびに CostA、文字列 B から1文字削除するたびに CostB のコストがかかります。どの文字を削除してもコストは一定です。例として、文字列 A = wxyz、B = wyzx、CostA = 10、CostB = 20 の場合を考えてみましょう。両方の文字列から「x」を削除すると、A と B はどちらも wyz となり一致します。このときの
-
C++で2つのリストの共通要素を求める方法|set_intersectionの使い方
二人がそれぞれ別々のリストに希望する都市を挙げているとしましょう。このとき、両者が共通して選んでいる都市を見つけたい場面はよくあります。プログラミングにおいて「2つの集合に共通する要素」を求める処理は頻出のタスクであり、C++では標準ライブラリのset_intersectionを使うことで簡単に実現できます。 共通要素を求める基本的な考え方 この操作は、数学における「集合の積(インターセクション)」と同じ性質を持っています。2つのリストをそれぞれ集合とみなし、その積集合を求めることで、両方のリストに含まれる要素だけを取り出すことができます。 ただし、std::set_intersectionを
-
C++で中点の座標を使って長方形の4つの頂点を求める方法
問題の概要長方形 ABCD があり、その中点 P と Q の座標、および長方形の辺の長さ L のみが分かっているとします。この課題の目的は、P と Q の座標および辺の長さ L を使って、頂点 A、B、C、D の座標を求めることです。例えば、P が (1, 0)、Q が (1, 2)、L が 2 の場合、A、B、C、D はそれぞれ (0, 0)、(0, 2)、(2, 2)、(2, 0) となります。考えられる3つの場合P と Q の位置関係によって、次の3つの場合が考えられます。長方形が水平な場合:AD と BC が X 軸に平行長方形が垂直な場合:AD と BC が Y 軸に平行長方形が軸に
-
C++で二分木における単一値の部分木を数える方法
二分木が与えられたとき、その木に含まれる「単一値の部分木(Single Valued Subtree)」の個数を求めるのが本記事の目的です。単一値の部分木とは、その部分木を構成するすべてのノードが同じ値を持つような部分木のことを指します。問題の例例として、次のような二分木を考えてみましょう。この木には、以下に示す4つの単一値の部分木が存在します。解法のアプローチ:ボトムアップ方式この問題は、ボトムアップ(下から上へ)の再帰的なアプローチで効率的に解くことができます。基本的な考え方は次のとおりです。各ノードを訪問する際、そのノードを根とする部分木が単一値であるかどうかを判定し、単一値であればカウ
-
C++で配列内の最大GCDを持つペアを検索する方法
問題の概要正の整数で構成される配列が与えられたとき、その中からGCD(最大公約数)が最大となる整数のペアを見つけるのがこの記事のテーマです。例として、配列 A = {1, 2, 3, 4, 5} を考えてみましょう。この場合の出力は 2 になります。ペア (2, 4) のGCDが 2 であり、それ以外のどのペアのGCDも 2 未満にしかならないためです。解法のアプローチこの問題を効率的に解くには、各約数の出現回数を記録するカウント配列を活用します。全体の流れは次の通りです。配列内の各要素について約数をすべて列挙し、カウント配列に記録します。1つの要素の約数列挙には O(√arr[i]) の時間
-
【C++】指定されたインデックスのN個のフィボナッチ数のGCDを効率的に求める方法
本記事では、指定された複数のインデックスに対応するN個のフィボナッチ数の最大公約数(GCD)を、C++で効率的に求める方法を解説します。 フィボナッチ数列と問題の概要 まずおさらいとして、フィボナッチ数列は「0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …」のように、直前の2つの項の和によって定義される数列です。インデックスは0から始まるため、0番目の要素は0、1番目の要素は1となります。 例えば、インデックス{2, 3, 4, 5}に対応するフィボナッチ数は{1, 2, 3, 5}であり、これらのGCDは1です。 鍵となる性質:GCD(Fibo(i), Fibo(j))
-
C++で回転ソート済み配列の回転回数を求める方法
ここでは、回転ソート済み配列(Rotated Sorted Array)が与えられたときに、その配列を元のソートされた状態に戻すために必要な回転回数を求める問題を扱います。なお、回転は「右から左へ」要素を移動させる操作として考えます。例えば、次のような配列を考えてみましょう。{15, 17, 1, 2, 6, 11}この配列をソートするには、2回の回転が必要です。回転を繰り返すと、最終的に次の順序になります。{1, 2, 6, 11, 15, 17}この場合の出力(回転回数)は 2 となります。解法のポイントこの問題のロジックは非常にシンプルです。配列を注意深く観察すると、必要な回転回数は「最
-
C++で指定範囲内の非推移的な互いに素なトリプルを見つける方法
問題の概要 下限(left)と上限(right)が与えられたとき、次の条件をすべて満たす非推移的なトリプル (x, y, z) を見つけることを考えます。 ペア (x, y) は互いに素である(最大公約数が1) ペア (y, z) も互いに素である しかし、ペア (x, z) は互いに素ではない 「互いに素」という性質は推移的ではないため、このような組み合わせが存在し得ます。例えば、下限が2、上限が10の場合、候補となる要素は {2, 3, 4, 5, 6, 7, 8, 9, 10} です。この中では (4, 7, 8) が条件を満たします。GCD(4, 7) = 1、GCD(7, 8)
-
C++でGCDを使わずに3つ以上の数(配列)の最小公倍数(LCM)を求める方法
配列Aが与えられたとき、GCD(最大公約数)の演算を使わずに、すべての要素のLCM(最小公倍数)を求める方法を解説します。例えば、配列が {4, 6, 12, 24, 30} の場合、LCMは120になります。2つの数値のLCMは簡単に計算できます。以下のアルゴリズムに従うことでLCMを求められます。getLCM(a, b) のアルゴリズムbegin if a > b, then m := a, otherwise m := b while true do if m is divisible by both a and b, then retur
-
C++で二分探索木(BST)からCeiling(天井)とFloor(床)を求める方法
本記事では、二分探索木(BST)からCeiling(天井)値とFloor(床)値を求める方法について解説します。まず用語を整理しておきましょう。あるキーに対する「Ceiling」とは、そのキー以上の値の中で最小の要素を指し、「Floor」とはそのキー以下の値の中で最大の要素を指します。応用例:メモリ管理システム例えば、メモリ管理システムを構築することを考えてみます。空きメモリブロック(フリーノード)がBST上に配置されており、入力された要求サイズに対して最適なフィット(ベストフィット)を見つけたい場面です。このとき、ツリーを降下しながら「キー値より大きい最小のデータ」を追跡していくことになりま
-
C++で配列内に他のすべての要素と互いに素な要素が存在するかを判定する方法
問題の概要 正の整数からなる配列 A[] が与えられます。ここで、各要素は 2 ≤ A[i] ≤ 106 を満たすものとします。求めたいのは、配列内に「他のすべての要素と互いに素(coprime)なペアを形成できる要素」が少なくとも1つ存在するかどうかの判定です。 例として、配列 {2, 8, 4, 10, 6, 7} を考えてみましょう。この場合、7 は配列内の他のすべての要素(2, 8, 4, 10, 6)と互いに素であるため、条件を満たす要素が存在します。 効率的な解法のアプローチ この問題を効率的に解く鍵は、配列内の各整数についてすべての素因数を求めることです。ある要素が他のどの要素
-
C++で式の括弧のバランスを判定する方法(スタックを使った実装例)
プログラミングにおいて、与えられた式に含まれる括弧の対応が正しく取れているか(バランスが取れているか)を判定することは、構文解析やコンパイラの実装などでよく登場する基本的な問題です。対象となる括弧は ()、{}、[] の3種類です。例えば、文字列「()[(){()}]」は有効ですが、「{[}]」は括弧の対応が崩れているため無効と判定されます。 この問題は、スタック(stack)というデータ構造を使うことでシンプルに解決できます。解法の手順は以下の通りです。 式を先頭から順に走査します。 現在の文字が開き括弧((、{、[)であれば、スタックにプッシュします。 現在の文字が閉じ括弧()、}、
-
C++で二分木の子ノード合計プロパティを検証する方法
二分木が与えられたとき、次のプロパティ(性質)を満たしていれば、その二分木は有効とみなされます。各ノードのデータ値は、左の子ノードと右の子ノードの値の合計と一致していなければなりません。どちらかの側に子ノードが存在しない場合は、その値は0として扱われます。例えば、以下のような木が与えられた場合、このプロパティを満たしていることになります。この性質を確認するための特別なトリックは存在せず、木を再帰的に走査する必要があります。ノードとその両方の子がプロパティを満たしていればtrueを返し、そうでなければfalseを返します。アルゴリズムの流れ検証は以下の手順で行われます。ノードがNULL、または葉
-
C++でBSTを構築せずに2つの配列が同一の二分探索木になるか判定する方法
ここでは、2つの配列がそれぞれBST(二分探索木)への挿入順序を表しているものとします。各配列の要素を左から右へ順に取り出してBSTを構築したとき、両者から同じ形の木ができるかどうかを判定します。ただし、実際にBSTを構築することは禁止されています。例えば、配列 {2, 4, 1, 3} と {2, 1, 4, 3} が与えられた場合、この2つの列はどちらも同じBSTを生成することが分かります。 アプローチはシンプルです。BSTには「根より小さい要素は左部分木に、根より大きい要素は右部分木に属する」という基本的な性質があります。したがって、2つの配列が同じBSTを表すためには、任意の要素
-
C++でソート済み配列の過半数要素(マジョリティ要素)を判定する方法
ソート済みの配列が与えられたとき、指定した数値 x がその配列の「過半数要素(majority element)」であるかどうかを判定する問題について解説します。 過半数要素(マジョリティ要素)とは ある要素が過半数要素であるとは、その要素が配列内に n/2 回より多く出現することを指します。ここで n は配列のサイズです。 例えば、配列 {1, 2, 3, 3, 3, 3, 6}、x = 3 の場合を考えてみましょう。この配列には 3 が 4 回出現しており、配列のサイズは 7 なので、4 > 7/2 = 3 となり、3 は過半数要素であると言えます。したがって答えは true にな