1+ <!-- 以下は、**PHP 8.2.8** を用いた「巡回セールスマン問題(TSP)」の解法です。
2+ ビットDP(状態:訪問済み都市集合×現在地)により、**全都市を訪問して出発地に戻る最短距離**を求めます。
3+
4+ ---
5+
6+ ## ✅ 実装(PHP 8.2.8、型・コメント付き)
7+
8+ <?php
9+
10+ declare (strict_types=1 );
11+
12+ /**
13+ * 距離行列を計算する
14+ *
15+ * @param array<int, array{int, int}> $coords 各都市の座標 [(x1, y1), ..., (xN, yN)]
16+ * @return array<int, array<int, float>> 距離行列 dist[i][j] = 都市iからjへの距離
17+ */
18+ function computeDistances (array $ coords ): array {
19+ $ N = count ($ coords );
20+ $ dist = array_fill (0 , $ N , array_fill (0 , $ N , 0.0 ));
21+ for ($ i = 0 ; $ i < $ N ; $ i ++) {
22+ for ($ j = 0 ; $ j < $ N ; $ j ++) {
23+ $ dx = $ coords [$ i ][0 ] - $ coords [$ j ][0 ];
24+ $ dy = $ coords [$ i ][1 ] - $ coords [$ j ][1 ];
25+ $ dist [$ i ][$ j ] = hypot ($ dx , $ dy );
26+ }
27+ }
28+ return $ dist ;
29+ }
30+
31+ /**
32+ * 巡回セールスマン問題を解く
33+ *
34+ * @param int $N 都市の数 (2 <= N <= 15)
35+ * @param array<int, array{int, int}> $coords 各都市の座標
36+ * @return float 最短距離(誤差1e-3以内)
37+ */
38+ function solveTSP (int $ N , array $ coords ): float {
39+ $ dist = computeDistances ($ coords );
40+ $ INF = INF ;
41+
42+ // dp[s][u] := 訪問済み集合s, 現在地u のときの最短距離
43+ $ dp = array_fill (0 , 1 << $ N , array_fill (0 , $ N , $ INF ));
44+ $ dp [1 ][0 ] = 0.0 ;
45+
46+ for ($ s = 1 ; $ s < (1 << $ N ); $ s ++) {
47+ for ($ u = 0 ; $ u < $ N ; $ u ++) {
48+ if (!(($ s >> $ u ) & 1 )) continue ;
49+ for ($ v = 0 ; $ v < $ N ; $ v ++) {
50+ if (($ s >> $ v ) & 1 ) continue ;
51+ $ ns = $ s | (1 << $ v );
52+ $ dp [$ ns ][$ v ] = min ($ dp [$ ns ][$ v ], $ dp [$ s ][$ u ] + $ dist [$ u ][$ v ]);
53+ }
54+ }
55+ }
56+
57+ $ res = $ INF ;
58+ for ($ u = 1 ; $ u < $ N ; $ u ++) {
59+ $ res = min ($ res , $ dp [(1 << $ N ) - 1 ][$ u ] + $ dist [$ u ][0 ]);
60+ }
61+
62+ return $ res ;
63+ }
64+
65+ /**
66+ * 標準入力から座標情報を読み取り、solveTSP を呼び出すメイン関数
67+ */
68+ function main (): void {
69+ [$ N ] = sscanf (trim (fgets (STDIN )), "%d " );
70+ $ coords = [];
71+
72+ for ($ i = 0 ; $ i < $ N ; $ i ++) {
73+ [$ x , $ y ] = sscanf (trim (fgets (STDIN )), "%d %d " );
74+ $ coords [] = [$ x , $ y ];
75+ }
76+
77+ $ result = solveTSP ($ N , $ coords );
78+
79+ // 小数点以下12桁で出力(誤差1e-3対策)
80+ printf ("%.12f \n" , $ result );
81+ }
82+
83+ main ();
84+
85+ // ## 🧪 実行方法
86+
87+ // ```bash
88+ // php main.php < input.txt
89+ // ```
90+
91+ // 例: `input.txt`
92+
93+ // ```
94+ // 4
95+ // 0 0
96+ // 0 1
97+ // 1 0
98+ // 1 1
99+ // ```
100+
101+ // 出力:
102+
103+ // ```
104+ // 4.000000000000
105+ // ```
106+
107+ // ---
108+
109+ // ## 📊 計算量と制約への対応
110+
111+ // | 項目 | 内容 |
112+ // | -------- | ------------------------------ |
113+ // | 時間計算量 | `O(N^2 * 2^N)`(N≦15で約50万) |
114+ // | 空間使用量 | `N * 2^N * float` ≒ 約4MiB |
115+ // | 精度 | `hypot` + `printf("%.12f")` 使用 |
116+ // | PHPバージョン | 8.2.8(型指定、INF、hypot利用可) |
117+
118+ // ---
119+
120+ // ## ✅ 特徴
121+
122+ // * 型コメント・戻り値明示(`array{int, int}` 型タプル風)
123+ // * 安全で高速な `hypot()` による距離計算
124+ // * 浮動小数誤差に強い出力形式(12桁)
125+ // * 計算量・メモリ制限に完全準拠(N ≦ 15)
126+
127+ // ---
128+
129+ // ご希望があれば、**経路復元付き実装** や **部分メモ化付きDFS版** なども提供可能です。お気軽にどうぞ!
0 commit comments