C++で長さNのバイナリ文字列のうち3つ以上の連続した1を含むものの個数を求める方法
問題の概要
整数Nが与えられたとき、長さNのすべての異なるバイナリ文字列のうち、少なくとも3つの連続した1を含むものの総数を求めます。例えば、N = 4の場合、条件を満たす文字列は「0111」「1110」「1111」の3つであるため、出力は3となります。
アプローチ:動的計画法(DP)
この問題は動的計画法を用いることで効率的に解くことができます。ここで、DP(i, x)を「長さiの文字列のうち、位置i+1からi+xまでにx個の連続した1が並んでいる文字列の数」と定義します。すると、漸化式は次のように表されます。
DP(i, x) = DP(i – 1, 0) + DP(i – 1, x + 1)
この漸化式は、位置iに0と1のどちらが来るかという事実に基づいています。
- 位置iに0が来る場合:(i – 1)番目の位置でのxの値は0になります
- 位置iに1が来る場合:(i – 1)番目の位置でのxの値は、位置iでのxの値に1を加えたものになります
C++での実装例
以下は、メモ化再帰を用いたC++による実装例です。
#include<iostream>
using namespace std;
int n;
int numberCount(int i, int x, int table[][4]) {
if (i < 0)
return x == 3;
if (table[i][x] != -1)
return table[i][x];
table[i][x] = numberCount(i - 1, 0, table);
table[i][x] += numberCount(i - 1, x + 1, table);
return table[i][x];
}
int main() {
n = 4;
int table[n][4];
for (int i = 0; i < n; i++)
for (int j = 0; j < 4; j++)
table[i][j] = -1;
for (int i = 0; i < n; i++) {
table[i][3] = (1 << (i + 1));
}
cout << "The number of binary strings with at least 3 consecutive 1s: " << numberCount(n - 1, 0, table);
}
コードのポイント
- numberCount関数:再帰的に文字列の数を数えます。i < 0になった時点で、xが3に達しているかどうか(x == 3)を判定して返します。
- メモ化テーブル:table配列で計算済みの値をキャッシュすることで、重複する計算を避け、計算量を大幅に削減しています。
- 初期化の工夫:table[i][3] = (1 << (i + 1)) は、すでに3つの連続した1を持つ文字列の残りの部分は0と1を自由に選べることを利用した初期化です。
出力結果
The number of binary strings with at least 3 consecutive 1s: 3
N = 4の場合、条件を満たす文字列「0111」「1110」「1111」が正しくカウントされ、3が出力されます。この手法を使えば、Nが大きい場合でも全列挙(2N通り)を避け、効率的に答えを求めることができます。
-
【C++】しきい値距離以内で到達できる都市数が最も少ない都市を求める方法
問題概要0からn-1までの番号が付けられたn個の都市があるとします。配列edgesが与えられ、edges[i] = [fromi, toi, weighti] は都市fromiとtoiの間を結ぶ双方向の重み付き辺を表します。さらに、整数の距離しきい値(distance threshold)が与えられます。このとき、何らかの経路を辿って到達でき、かつその距離がしきい値以下となる都市の数が最も少ない都市を求めてください。該当する都市が複数存在する場合は、その中で最も番号の大きい都市を返します。入力例次のような入力を考えてみましょう。n = 4、距離しきい値も4であるとき、出力は3になります。その理
-
C++の二分探索木(BST)で最小値のノードを見つける方法
二分探索木(Binary Search Tree、BST)が与えられたとき、その木の中から最小の要素を見つけることを考えます。例えば、以下のようなBSTがあるとします。この場合、最小要素は 1 になります。考え方二分探索木の重要な性質として、左部分木には必ず親ノードより小さい値が格納されるというものがあります。この性質を利用すると、次の手順で最小要素を見つけることができます。ルートノードから探索を開始します。現在のノードの左の子が NULL でない間、左の子へ移動を繰り返します。左の子が NULL になったノードの値が、木全体の中で最小の要素です。この操作の計算量は木の高さに依存し、平衡な二分