√n約数列挙+桁和比較 による O(√n + d·log n) 実装
Kristenは数字の各桁の和(桁和)で数の良し悪しを判定します。与えられた整数
n
の約数のうち、以下の基準で「最良」のものを見つけてください:
Input:
12
Output:
6
説明: 12の約数は {1, 2, 3, 4, 6, 12}。各桁和は {1, 2, 3, 4, 6, 3}。最大桁和6を持つ約数は 6。
max() と
sum() を活用
def findBestDivisor(n: int) -> int:
"""
最良の約数を見つける(競技プログラミング最適化版)
Time Complexity: O(√n)
Space Complexity: O(d) where d is number of divisors
"""
divisors = []
i = 1
# √n まで探索して約数をペアで収集
while i * i <= n:
if n % i == 0:
divisors.append(i)
# 平方数でない場合のみペアを追加
if i != n // i:
divisors.append(n // i)
i += 1
# 桁和が最大、同値なら最小値を選択
# タプル比較: (桁和大, 値小) = (sum, -divisor)
return max(divisors, key=lambda x: (sum(int(d) for d in str(x)), -x))
# 使用例
if __name__ == '__main__':
n = int(input().strip())
result = findBestDivisor(n)
print(result)
フローの説明:
1. 入力 n を受け取り、空の約数リスト divisors と i=1 で初期化
2. i×i ≤ n の間ループ:n を i で割り切れるか判定
3. 割り切れる場合、i と n//i を約数リストに追加(i≠n//i の場合のみペア追加)
4. i を1増やしてループ継続
5. ループ終了後、約数リストから桁和が最大(同値なら最小値)の約数を選択
6. 結果を返して終了
| 項目 | 本実装(√n列挙) | 代替手法(全探索) |
|---|---|---|
| 時間計算量 |
O(√n + d·log n)
√n までループ + 各約数の桁和計算 O(log n)
|
O(n)
1 から n まで全探索
|
| 空間計算量 |
O(d)
約数リスト(d は約数の個数)
|
O(d)
同じ
|
| 実装コスト |
中
√n 判定とペア追加が必要
|
低
シンプルなループ
|
| 制約 n≤105 での推奨度 |
★★★★★
最大√100000 ≈ 316 回のループで済む
|
★★★☆☆
100000回ループは許容範囲だが非効率
|
max()
のkey引数で、タプル比較
(桁和, -値)
により1パスで最良約数を選択
sum(int(d) for d in str(x))
でジェネレータ式とC実装のsumを活用
i != n // i
で重複を防ぐ(例:n=16 の場合 i=4 を2回追加しない)
n = 12 の場合: