Pythonでn人がスイッチを切り替えた後に点灯しているライトの数を求めるプログラム
部屋の中にn個のトグルスイッチがあり、そこにn人がいるとします。彼らは以下のルールに従ってスイッチを切り替えていきます。
- 1番目の人は、すべてのスイッチを切り替えます。
- 2番目の人は、2の倍数にあたるスイッチ(2, 4, 6, ...)を切り替えます。
- i番目の人は、iの倍数にあたるスイッチを切り替えます。以降も同様です。
このとき、最終的にONの状態になっているスイッチの数を求める必要があります。
例えば、入力がn = 5の場合、出力は2になります。初期状態では電球は [0, 0, 0, 0, 0] です。
- 1番目の人の操作後: [1, 1, 1, 1, 1]
- 2番目の人の操作後: [1, 0, 1, 0, 1]
- 3番目の人の操作後: [1, 0, 0, 0, 1]
- 4番目の人の操作後: [1, 0, 0, 1, 1]
- 5番目の人の操作後: [1, 0, 0, 1, 0]
最終的にONになっているライトは2つあることがわかります。
解法のアプローチ
この問題を解くために、以下の手順に従います。
- l := 0 とします。
- r := n とします。
- l <= r の間、以下を繰り返します。
- mid := l + (r - l) / 2 の小数点以下切り捨て
- mid * mid <= n < (mid + 1)2 が成り立つ場合、mid を返します。
- n < mid2 の場合、r := mid とします。
- それ以外の場合、l := mid + 1 とします。
なぜ平方根で答えが求まるのか?
k番目のスイッチが切り替えられる回数は、kの約数の個数と一致します。そして、約数の個数が奇数になるのは、kが完全平方数の場合だけです。したがって、最終的にONになっているスイッチの数は「n以下の完全平方数の個数」、すなわち ⌊√n⌋ に等しくなります。上記のコードでは、二分探索を用いてnの整数平方根を効率的に計算しています。
実装例
理解を深めるために、以下の実装を見てみましょう。
def solve(n):
l, r = 0, n
while l <= r:
mid = l + (r - l) // 2
if mid * mid <= n < (mid + 1) * (mid + 1):
return mid
elif n < mid * mid:
r = mid
else:
l = mid + 1
n = 5
print(solve(n))
入力
5
出力
2
-
Pythonでn個のノードから構成できる二分探索木(BST)の数を求める方法
問題の概要互いに異なるn個のノードが与えられたとき、それらを二分探索木(BST:Binary Search Tree)として配置する方法が何通りあるかを求めることを考えます。二分探索木には「左部分木には常に親より小さい値が、右部分木には常に親より大きい値が格納される」という重要な性質があります。この問題を解くには、カタラン数(Catalan Number)を利用します。カタラン数 C(n) は、n個の異なるキーから構成できる二分探索木の総数を正確に表すことが知られています。計算式は次のとおりです。$$C(n)=\frac{(2n)!}{(n+1)!\times n!}$$例えば、入力が n =
-
Pythonで二分木の合計がkとなるパスの数を数える方法
問題の概要 二分木と値 k が与えられたとき、あるノードからその子孫へ向かうパスのうち、通過するノードの値の合計がちょうど k と一致するものがいくつ存在するかを求める問題です。 例えば、次のような二分木を考えてみましょう。 このとき k = 5 であれば、出力は 2 となります。条件を満たすパスは [2, 3] と [1, 4] の2つだからです。 解き方のアプローチ:累積和(prefix sum)の活用 この問題は「累積和(prefix sum)」というテクニックを使うことで、全ノードを一度だけ訪問する効率的なアルゴリズムとして解けます。考え方の手順は以下の通りです。 count:マッ