動的計画法を極める
ビット DP
参考資料:https://www.slideshare.net/hcpc_hokudai/advanced-dp-2016 (opens in a new tab)
巡回セールスマン問題
- 閉路であればどこからスタートしてもよいので、始点と終点を 0 に固定して考える
# 巡回セールスマン問題
import functools
INF = 2**64
n: int
d: list[list[int]]
@functools.cache()
def dp(s, v) -> int:
if s == (1 << n) - 1 and v == 0:
return 0
res = INF
for u in range(n):
if s >> u & 1 == 0:
res = min(res, dp(s | 1 << u, u) + d[v][u])
return res
def solve() -> None:
print(dp(0, 0))
>>> n = 5
>>> d = [
... [INF, 3, INF, 4, INF],
... [INF, INF, 5, INF, INF],
... [4, INF, INF, 5, INF],
... [INF, INF, INF, INF, 4],
... [7, 6, INF, INF, INF],
... ]
>>> solve()
23
Traveling by Stagecoach
- 「状態」を頂点、「状態遷移」を辺とするグラフを考える
- 状態:都市 にいて、残っている乗車券の集合が である
- 遷移:乗車券 を用いて、一本の道路で結ばれた都市 に移動する
- この時のコストは、 の距離 /
# Traveling by Stagecoach
...
ドミノ敷き詰め
- 境界が同じならその後の詰め方は同じ
- 境界をビット列で管理すればよい
- を考える時に、自分より左のものは既に埋まっていると考える
# ドミノ敷き詰め
...
練習問題
行列累乗
フィボナッチ数列
とすると、
# フィボナッチ数列
import numpy as np
n: int
def solve() -> None:
a = np.array(
[
[1, 1],
[1, 0],
]
)
A = (np.linalg.matrix_power(a, n) @ np.array([[1], [0]]))[1][0] % 10**4
print(A)
>>> n = 10
>>> solve()
55
一般の 項間漸化式の場合、漸化式を
とすると、行列を用いて
と表せる。漸化式に定数項がある場合は、
となる。
Blocks
左から順に塗る。 個目までを、
- 赤・ 緑ともに偶数個になるように塗る総数を
- 赤・ 緑 の片方のみが奇数個となるように塗る総数を
- 赤・ 緑ともに奇数個になるように塗る総数を
とすると、 個目までを赤・緑ともに奇数個となるように塗るには、
- 個目までを赤・緑ともに奇数個になるように塗った上で、 個目を青もしくは黄色で塗る
- 個目までを赤・緑の片方が奇数個となるように塗った上で、 個目を赤・緑のうち奇数個の方で塗る
の 2 通りがあり、
が成り立つ。同様にして、
も成り立つ。
# Blocks
import numpy as np
n: int
def solve() -> None:
a = np.array(
[
[2, 1, 0],
[2, 2, 2],
[0, 1, 2],
]
)
A = np.linalg.matrix_power(a, n)[0][0] % 10**4
print(A)
>>> n = 1
>>> solve()
2
>>> n = 2
>>> solve()
6
グラフの長さ のパスの列挙
- パスの個数と パスの個数を掛け合わせると、 パスの個数となる
- 上式は行列の積の定義そのもの
# グラフの長さkのパスの個数
...
Matrix Power Series
# Matrix Power Series
...
データ構造を用いて高速化
...