Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/bitDP/B23/B23.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// 以下は、**Go 1.20.6** を用いた巡回セールスマン問題(TSP)の解法です。
// ビットDPを用いて、全都市を一度ずつ訪問し出発地点に戻る最短距離を求めます。

// ---

// ## ✅ Go実装(main.go)

// ```go
package main

import (
"bufio"
"fmt"
"math"
"os"
"strconv"
"strings"
)

// Point 座標を表す構造体
type Point struct {
x int
y int
}

// computeDistances は各都市間のユークリッド距離を前計算して返します。
//
// 引数:
// coords []Point: 都市の座標
//
// 戻り値:
// [][]float64: dist[i][j] := 都市iから都市jへの距離
func computeDistances(coords []Point) [][]float64 {
N := len(coords)
dist := make([][]float64, N)
for i := 0; i < N; i++ {
dist[i] = make([]float64, N)
for j := 0; j < N; j++ {
dx := float64(coords[i].x - coords[j].x)
dy := float64(coords[i].y - coords[j].y)
dist[i][j] = math.Hypot(dx, dy)
}
}
return dist
}

// solveTSP はビットDPを使って巡回セールスマン問題の最短距離を返します。
//
// 引数:
// N int: 都市の数 (2 <= N <= 15)
// coords []Point: 都市の座標
//
// 戻り値:
// float64: 最短経路の長さ(誤差1e-3未満)
func solveTSP(N int, coords []Point) float64 {
const INF = 1e18
dist := computeDistances(coords)

dp := make([][]float64, 1<<N)
for i := range dp {
dp[i] = make([]float64, N)
for j := range dp[i] {
dp[i][j] = INF
}
}
dp[1][0] = 0.0 // 都市0のみ訪問している状態

for s := 1; s < (1 << N); s++ {
for u := 0; u < N; u++ {
if (s>>u)&1 == 0 {
continue
}
for v := 0; v < N; v++ {
if (s>>v)&1 != 0 {
continue
}
ns := s | (1 << v)
if dp[ns][v] > dp[s][u]+dist[u][v] {
dp[ns][v] = dp[s][u] + dist[u][v]
}
}
}
}

// 全都市訪問後、都市0へ戻る
res := INF
full := (1 << N) - 1
for u := 1; u < N; u++ {
if cost := dp[full][u] + dist[u][0]; cost < res {
res = cost
}
}
return res
}

// main は標準入力を読み取り、TSPの最短距離を出力します。
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
N, _ := strconv.Atoi(scanner.Text())

coords := make([]Point, N)
for i := 0; i < N; i++ {
scanner.Scan()
fields := strings.Fields(scanner.Text())
x, _ := strconv.Atoi(fields[0])
y, _ := strconv.Atoi(fields[1])
coords[i] = Point{x, y}
}

result := solveTSP(N, coords)
fmt.Printf("%.12f\n", result)
}

// ## ✅ 実行方法

// ```bash
// go run main.go < input.txt
// ```

// ### 入力例(input.txt):

// ```
// 4
// 0 0
// 0 1
// 1 0
// 1 1
// ```

// ### 出力:

// ```
// 4.000000000000
// ```

// ---

// ## 📊 処理量とメモリの見積もり

// | 指標 | 値 |
// | -------- | ------------------------------- |
// | 時間計算量 | `O(N^2 * 2^N)`(N=15で約50万回) |
// | 空間使用量 | 約 `N * 2^N * 8 Byte` ≒ 最大 4 MiB |
// | 浮動小数誤差対策 | `math.Hypot` と `%.12f` で安全な出力 |

// ---

// ## ✅ この実装の特徴

// * `math.Hypot(dx, dy)` による安全で精度の高い距離計算
// * `[]float64` による省メモリなDPテーブル
// * 都市数 N=15 まで十分高速に対応
// * 誤差 1e-3 未満の出力精度を保証(`fmt.Printf("%.12f")`)

// ---
80 changes: 80 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/bitDP/B23/B23.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 以下は、Node.js (18.16.1) 環境で動作する、\*\*巡回セールスマン問題(TSP)\*\*をビットDPで解く実装です。
// 距離は **ユークリッド距離**で計算し、すべての都市を訪れて元の都市に戻る最短距離を求めます。

// ```javascript
// Node.js (v18.16.1) における TSP の解法(ビットDP)
// 実行時間制限: 10秒、メモリ制限: 1024MiB

const fs = require('fs');

/**
* 都市巡回セールスマン問題を解く関数
* @param {number} N - 都市の数
* @param {[number, number][]} coords - 都市の座標配列 (X, Y)
* @returns {number} - 最短距離(絶対/相対誤差10^-3未満で正解)
*/
function solveTSP(N, coords) {
const dist = Array.from({ length: N }, () => Array(N).fill(0));

// 距離を前計算
for (let i = 0; i < N; i++) {
for (let j = 0; j < N; j++) {
const dx = coords[i][0] - coords[j][0];
const dy = coords[i][1] - coords[j][1];
dist[i][j] = Math.hypot(dx, dy); // √(dx² + dy²)
}
}

const INF = Infinity;
const dp = Array.from({ length: 1 << N }, () => Array(N).fill(INF));

// 初期位置を全都市として開始(0都市からスタート)
dp[1][0] = 0;

for (let s = 1; s < (1 << N); s++) {
for (let u = 0; u < N; u++) {
if (!(s & (1 << u))) continue;
for (let v = 0; v < N; v++) {
if (s & (1 << v)) continue;
const ns = s | (1 << v);
dp[ns][v] = Math.min(dp[ns][v], dp[s][u] + dist[u][v]);
}
}
}

// 最後に戻る距離を加える
let res = INF;
for (let u = 1; u < N; u++) {
res = Math.min(res, dp[(1 << N) - 1][u] + dist[u][0]);
}

return res;
}

// 入力処理
(function main() {
const input = fs.readFileSync('/dev/stdin', 'utf8').trim().split('\n');
const N = parseInt(input[0], 10);
const coords = input.slice(1).map(line => line.split(' ').map(Number));

const result = solveTSP(N, coords);

// 絶対誤差・相対誤差が 1e-3 未満になるように出力(12桁精度)
console.log(result.toFixed(12));
})();

// ### ✅ 解説

// * **計算量**: `O(N^2 * 2^N)`(N=15 でも問題なし)
// * **距離の計算**: `Math.hypot(dx, dy)` を使うと精度良くユークリッド距離を計算できます。
// * **メモリ使用量**: 約 `N * 2^N * 8 byte`(最大約 4 MiB 程度)
// * **誤差対策**: `toFixed(12)` で小数第12位まで出力

// ### ✅ 実行例(入力: `input.txt` を指定)

// ```bash
// $ node main.js < input.txt
// 4.000000000000
// ```

// 他の改善(例えば枝刈りやメモ化DFS)もありますが、今回の制約ならこのビットDPが最適です。必要があれば経路復元付きの実装も可能です。
129 changes: 129 additions & 0 deletions Algorithm/DynamicProgramming/atCoder/bitDP/B23/B23.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<!-- 以下は、**PHP 8.2.8** を用いた「巡回セールスマン問題(TSP)」の解法です。
ビットDP(状態:訪問済み都市集合×現在地)により、**全都市を訪問して出発地に戻る最短距離**を求めます。

---

## ✅ 実装(PHP 8.2.8、型・コメント付き)

<?php

declare(strict_types=1);

/**
* 距離行列を計算する
*
* @param array<int, array{int, int}> $coords 各都市の座標 [(x1, y1), ..., (xN, yN)]
* @return array<int, array<int, float>> 距離行列 dist[i][j] = 都市iからjへの距離
*/
function computeDistances(array $coords): array {
$N = count($coords);
$dist = array_fill(0, $N, array_fill(0, $N, 0.0));
for ($i = 0; $i < $N; $i++) {
for ($j = 0; $j < $N; $j++) {
$dx = $coords[$i][0] - $coords[$j][0];
$dy = $coords[$i][1] - $coords[$j][1];
$dist[$i][$j] = hypot($dx, $dy);
}
}
return $dist;
}

/**
* 巡回セールスマン問題を解く
*
* @param int $N 都市の数 (2 <= N <= 15)
* @param array<int, array{int, int}> $coords 各都市の座標
* @return float 最短距離(誤差1e-3以内)
*/
function solveTSP(int $N, array $coords): float {
$dist = computeDistances($coords);
$INF = INF;

// dp[s][u] := 訪問済み集合s, 現在地u のときの最短距離
$dp = array_fill(0, 1 << $N, array_fill(0, $N, $INF));
$dp[1][0] = 0.0;

for ($s = 1; $s < (1 << $N); $s++) {
for ($u = 0; $u < $N; $u++) {
if (!(($s >> $u) & 1)) continue;
for ($v = 0; $v < $N; $v++) {
if (($s >> $v) & 1) continue;
$ns = $s | (1 << $v);
$dp[$ns][$v] = min($dp[$ns][$v], $dp[$s][$u] + $dist[$u][$v]);
}
}
}

$res = $INF;
for ($u = 1; $u < $N; $u++) {
$res = min($res, $dp[(1 << $N) - 1][$u] + $dist[$u][0]);
}

return $res;
}

/**
* 標準入力から座標情報を読み取り、solveTSP を呼び出すメイン関数
*/
function main(): void {
[$N] = sscanf(trim(fgets(STDIN)), "%d");
$coords = [];

for ($i = 0; $i < $N; $i++) {
[$x, $y] = sscanf(trim(fgets(STDIN)), "%d %d");
$coords[] = [$x, $y];
}

$result = solveTSP($N, $coords);

// 小数点以下12桁で出力(誤差1e-3対策)
printf("%.12f\n", $result);
}

main();

// ## 🧪 実行方法

// ```bash
// php main.php < input.txt
// ```

// 例: `input.txt`

// ```
// 4
// 0 0
// 0 1
// 1 0
// 1 1
// ```

// 出力:

// ```
// 4.000000000000
// ```

// ---

// ## 📊 計算量と制約への対応

// | 項目 | 内容 |
// | -------- | ------------------------------ |
// | 時間計算量 | `O(N^2 * 2^N)`(N≦15で約50万) |
// | 空間使用量 | `N * 2^N * float` ≒ 約4MiB |
// | 精度 | `hypot` + `printf("%.12f")` 使用 |
// | PHPバージョン | 8.2.8(型指定、INF、hypot利用可) |

// ---

// ## ✅ 特徴

// * 型コメント・戻り値明示(`array{int, int}` 型タプル風)
// * 安全で高速な `hypot()` による距離計算
// * 浮動小数誤差に強い出力形式(12桁)
// * 計算量・メモリ制限に完全準拠(N ≦ 15)

// ---

// ご希望があれば、**経路復元付き実装** や **部分メモ化付きDFS版** なども提供可能です。お気軽にどうぞ!
Loading