1. paizaラーニングトップ
  2. レベルアップ問題集
  3. 巡回セールスマン問題メニュー(言語選択)
  4. 問題一覧 F#(Beta)編
  5. 禁断探索法によるTSP F#(Beta)編

巡回セールスマン問題メニューのサムネイル
禁断探索法によるTSP F#(Beta)編(paizaランク S 相当)

問題にチャレンジして、ユーザー同士で解答を教え合ったり、コードを公開してみよう!

問題

下記の問題をプログラミングしてみよう!

巡回セールスマン問題とは、都市の集合と各都市間の距離が与えられ、全都市をちょうど1回ずつ訪れたのち出発した都市に戻ってくるような経路 (巡回路) のうち最も短いものを求める問題です。

ここでは、禁断探索法と呼ばれるアルゴリズムを学習しましょう。このアルゴリズムは発見的解法 (ヒューリスティクス) と呼ばれるもので、ある程度良い解が出力されることが期待されるものの、解の精度は全く保証されません。

一般的な禁断探索法の概要は、以下の通りです。

・ 解を適当に作り、暫定解 X とする。最良解 B を X で初期化する。禁断リストを空で初期化する。禁断リストのサイズの上限 TL を決める
・ 一定の回数、以下を繰り返す
・ (*) 暫定解 X を少しだけ変更したもの (X の近傍) であって、なおかつ禁断リストに含まれていない解のうち最も良い解 X' を求め、X を X' で更新する。X が B より真に良い場合は、B を X で更新する。解 X を禁断リストの先頭に加える。禁断リストのサイズが TL を超えた場合は禁断リストに含まれている最も古い解を削除する
・ 最良解 B を解として出力する

禁断リストを用いることによって、探索済みの解にすぐ戻ってしまう (= 探索がループしてしまう) ことが少なくなることが期待されます。このようにすることで、探索が局所解で停滞してしまうのを防いでいます。

この禁断探索法を用いて巡回セールスマン問題を解くことを考えます。まず、巡回路 X の近傍をどうするか、また禁断リストに何を入れるのか、を決める必要があります。ここでは、以下のようにします。
・ 巡回路 X に含まれる 2 本の辺を繋ぎ変えたものを X の近傍とする
・ 禁断リストには繋ぎ変えた辺の端点のうち巡回路 X の先頭に最も近い都市 t を入れる。つまり、t を端点として持つ辺を繋ぎ変えて解を更新した後しばらくは、t を端点として持つ辺は繋ぎ変えない



辺の繋ぎ変え方は 2-opt 法と同様です。例えば、辺 (a, d) と (b, c) を繋ぎ変えると 辺 (a, b) と (c, d) になります。



n 個の都市 (都市 0、都市 1、...、都市 n-1) のデータと、n 個の都市を巡る初期巡回路、整数 q、tl が与えられます。禁断探索法を実装し、巡回路を求めてください。(*) の処理は q 回繰り返してください。禁断リストのサイズは tl としてください。

ジャッジの都合上、以下の疑似コードに従って実装してください。SWAP(a, d, b, c)は巡回路 tour の辺 (a, d) と (b, c) を繋ぎ変えて辺 (a, b) と (c, d) にする処理を表します。

void tabu_search(){
// 暫定解
tour = (初期巡回路)
// 最良解
tour_best = tour
// 禁断リスト
tabu_list = (空のキュー)

for(int i = 0; i < q; i++){
a_best, b_best, diff = 99999999

// 暫定解 tour の近傍を全探索する
for(int a = 0; a < n; a++){
if(tour[a] in tabu_list){
// 都市 tour[a] を端点として持つ辺を繋ぎ変えてからあまり時間が経っていない場合は、tour[a] を端点として持つ辺は繋ぎ変えない
continue;
}
for(int b = a+2; b < n; b++){
if(a == 0 && b == n-1){
// 繋ぎ変える 2 辺の端点が重複してしまう場合を除外する
continue;
}
tmp_diff = (都市 tour[a] と tour[a+1] を結ぶ辺と、都市 tour[b] と tour[(b+1)%n] を結ぶ辺を結ぶ辺を繋ぎ変えた際の巡回路長の変化)
if(diff > tmp_diff){
diff = tmp_diff;
a_best = a, b_best = b;
}
}
}

// タブーリストを更新する
tabu_list.push(tour[a_best]);
if(tabu_list.size > tl){
tabu_list.pop()
}

// 暫定解 tour の近傍のうち最も良い解を新たな暫定解とする
SWAP(tour[a_best], tour[a_best+1], tour[b_best], tour[(b_best + 1)%n]);

if(tour の巡回路長が tour_best の巡回路長より短い){
tour_best = tour;
}
}

// tour を解として出力
}


なお、各都市は二次元平面上の点として与えられ、都市間の距離にはユークリッド距離を用いるものとします。

巡回路の始点は任意です。巡回路が正しければ正解と判定されます。

入力される値

n q tl
x_0 y_0
x_1 y_1
...
x_{n-1} y_{n-1}
p_0
p_1
...
p_{n-1}

・ 1 行目に、都市の個数 n と整数 q、tl が与えられます。
・ 2 行目から、n 行にわたり n 個の都市の座標が与えられます。
・ n+2 行目から、n 行にわたり初期巡回路が与えられます。


入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。 標準入力からの値取得方法はこちらをご確認ください
期待する出力

出力は n 行になります。(*) の処理を q 回繰り返した結果得られる巡回路を出力してください。巡回路は都市番号 (0, 1, ... , n-1) の順列で表し、都市番号を先頭から順に各行に 1 つずつ出力してください。

条件

すべてのテストケースにおいて、以下の条件をみたします。
・ 入力はすべて整数
・ 100 ≦ n ≦ 200
・ 100 ≦ q ≦ 200
・ 5 ≦ tl ≦ 20
・ -1,000 ≦ x_i, y_i ≦ 1,000 (0 ≦ i ≦ n-1)
・ i ≠ j ならば (x_i, y_i) ≠ (x_j, y_j)
・ p は {0, 1, ... , n-1} を並べ替えた順列

入力例1

100 100 5
-903 604
758 98
489 -60
155 480
-582 650
963 -868
988 -989
-689 305
896 -560
212 135
-901 333
-967 485
-470 -462
186 314
-73 -309
-973 -244
463 691
-286 365
46 526
293 -161
571 837
398 -992
124 -606
-379 335
-540 -844
853 504
-711 465
-458 717
434 -757
-952 304
254 575
360 644
-912 -43
-712 262
45 -798
-161 296
-859 -274
-101 590
-678 171
219 334
-207 447
241 535
256 -27
560 -926
-272 214
-533 480
-598 -683
757 -233
94 381
105 931
-142 398
-782 271
-454 -210
436 -833
369 -298
956 612
-133 937
-421 -600
-506 21
409 481
910 -646
427 -594
84 -793
732 384
40 611
-82 -207
5 580
-861 -261
937 -466
211 619
203 -689
912 736
-646 -169
750 311
334 0
862 -871
289 -254
-530 -355
242 -203
-59 551
637 -209
291 150
-595 204
-229 -446
729 343
158 -893
987 -817
564 -219
248 928
685 -46
90 -300
288 46
129 -362
-245 615
-801 -213
-420 431
-521 558
-876 645
551 -749
-540 -855
88
20
72
38
98
10
2
48
50
62
32
58
29
47
70
42
24
81
78
4
17
94
53
76
45
89
26
22
95
66
84
46
41
27
6
85
11
77
14
37
69
60
55
86
96
3
99
91
18
57
74
44
67
8
34
59
40
82
15
49
19
64
75
9
1
16
33
80
87
12
21
39
43
90
83
92
54
93
23
30
61
35
13
28
25
79
56
68
73
71
65
63
5
52
36
31
7
97
51
0

出力例1

88
20
16
31
30
69
3
41
59
39
13
48
9
81
91
74
42
78
76
54
19
2
84
63
71
55
25
73
1
89
87
80
47
68
8
60
86
5
6
75
43
98
61
28
53
21
85
34
62
70
22
92
90
65
14
83
46
99
24
57
12
77
52
72
94
67
36
15
32
29
10
11
0
97
4
27
96
45
26
7
33
51
38
82
58
23
95
17
44
35
50
40
93
37
79
66
18
64
56
49

問題一覧へ戻る

ページの先頭へ戻る