質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1115閲覧

幅優先探索の計算量削減

kay_ventris4

総合スコア269

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/12 03:09

#問題
ABC007C 幅優先探索

#方針
「Pythonではじめるアルゴリズム入門」(増井敏克著)のコードの書き方に倣い、実装をしました。
障壁部分は9, 未踏部分は0, ゴール部分は1, 既踏部分は2で更新するとします。

#コード

Python

1from collections import deque 2 3R, C = map(int, input().split()) 4sy, sx = map(int, input().split()) 5gy, gx = map(int, input().split()) 6 7maze = [] 8for _ in range(R): 9 li = list(str(input())) 10 li = [9 if el == '#' else 0 for el in li] 11 maze.append(li) 12 13maze[gy - 1][gx - 1] = 1 14 15data = deque([[sy - 1, sx - 1, 0]]) 16while data[0]: 17 x, y, d = data.popleft() 18 19 if maze[x][y] == 1: 20 print(d) 21 break 22 23 maze[x][y] = 2 24 25 if maze[x - 1][y] < 2: 26 data.append([x - 1, y, d + 1]) 27 if maze[x + 1][y] < 2: 28 data.append([x + 1, y, d + 1]) 29 if maze[x][y - 1] < 2: 30 data.append([x, y - 1, d + 1]) 31 if maze[x][y + 1] < 2: 32 data.append([x, y + 1, d + 1])

#質問
上のコードを提出すると、半分程度のテストケースで実行時間(2s)超過がおきました。
問題の制約は 1 <= R, C <= 50 程度なので、計算量も高が知れていると考えましたが、上のコードだとどのような箇所が計算時間を長くさせているのでしょうか。元の本のコードではdataの管理にリストを用いていましたが、dequeによる管理に切り替えてもさほど芳しい結果は得られませんでした。またサンプルとなる正解コードを複数見ても、アプローチが非常に多種多様で、自分のコードにおける問題点がはっきり見えませんでした。幅優先探索に関する学習を始めて日が浅いため、やや丸投げのような形にはなってしまいましたが、どのような部分に着眼すれば良いかといったヒントだけでも頂けますと幸いです。素人質問にて恐縮ですが、お力添え頂ける箇所がございましたら、ご教授のほど何卒よろしくお願い申し上げます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

この実装だと,あるマスが探索リスト(queue)に複数回登録され得るのでは.

例えば,

S A B C

というマップがあって,Sから処理が始まるとき,

  • Sの隣接マス A と B をリストに追加.
  • リストからAを取り出し,Cをリストに追加
  • リストからBを取り出し,Cをリストに追加←ここでCがダブる

あるマスが
「リストから取り出された」ことはマークされているが,
「リストに追加された」ことは把握されていないと見えるので,
多重登録され,多重の探索が行われるように思える.

投稿2021/07/12 03:40

編集2021/07/12 03:44
fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kay_ventris4

2021/07/12 05:19

ご回答ありがとうございます。 探索ノードを別途辞書で管理し、一度見たものであればもう探索不要としたところ大幅に計算量を削減してACすることが出来ました。
fana

2021/07/12 05:23

> maze[x][y] = 2 この,2という値でマークする処理を, 「リストから取り出された」際に行うのではなくて, 「リストに追加する」際に行うようにすればどうでしょう?
kay_ventris4

2021/07/12 05:36

if maze[x - 1][y] < 2:   data.append([x - 1, y, d + 1])   maze[x][y] = 2 ... とするということですか?(おそらく意図が汲みきれていません。申し訳ございません。)
fana

2021/07/12 05:44

if maze[x - 1][y] < 2:   data.append([x - 1, y, d + 1])   maze[x-1][y] = 2  ←ココ かな. 2という値でマークする処理の目的と言うのは 「各マスを2回以上探索しない」ことであり,そのためには「各マスはリストに1度しか登録されない」形とすれば良いのではなかろうか,と.
fana

2021/07/12 05:46

(スタート地点もループに入る前に2にマークしておくことを忘れずに)
fana

2021/07/12 05:50

質問文のコードだと,maze[x][y]の値が2というのは 「もうこのマス(x,y)は調べました」という意味だけど, これを 「もうこのマス(x,y)は探索リストに追加されました」という意味に変える.
kay_ventris4

2021/07/12 07:13

仰ることは理解致しましたが、maze[sy - 1][sx - 1] = 2 として、さらに if maze[x - 1][y] < 2:   data.append([x - 1, y, d + 1])   maze[x - 1][y] = 2 if maze[x + 1][y] < 2:   data.append([x + 1, y, d + 1])   maze[x + 1][y] = 2 if maze[x][y - 1] < 2:   data.append([x, y - 1, d + 1])   maze[x][y - 1] = 2 if maze[x][y + 1] < 2:   data.append([x, y + 1, d + 1])   maze[x][y + 1] = 2 としたところ、答えが何も出力されないということが発生しましたので、x, y, d = data.popleft()下のprint(d)部分を削除し、代わりにans = []を準備しans.append(d)として最後にprint(ans[-1])として対応したところ、半数程度のケースでWAとなりました。
fana

2021/07/12 08:08

ああ,ごめん. mazeを2にした際にゴールの1を上書きして消してしまうわけだ. ゴール判定を maze[x][y] == 1 じゃなくて,「座標(x,y)がゴール座標か?」という座標値での判定にすれば良いかと.
kay_ventris4

2021/07/12 08:12

うまく行きました。 最後までご丁寧にありがとうございました。
guest

0

単純に以下の判定を追加するだけでよいように思います。

Python

1while data[0]: 2 x, y, d = data.popleft() 3 4 if maze[x][y] == 1: 5 print(d) 6 break 7 8 # 探索済み(や壁)はそれ以上の探索は不要 9 if maze[x][y] >= 2: 10 continue 11 12 maze[x][y] = 2

投稿2021/07/12 07:22

can110

総合スコア38341

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kay_ventris4

2021/07/12 07:37

なるほど、踏破したところは最初から処理しないということですね。こちらは実装も非常でシンプルで自己解決方法よりもさらに高速に処理することが出来ました。ありがとうございました。
guest

0

解答を頂いたお二方のアプローチをそれぞれ以下に示しておきます:

解法①

Python

1from collections import deque 2 3R, C = map(int, input().split()) 4sy, sx = map(int, input().split()) 5gy, gx = map(int, input().split()) 6 7maze = [] 8for _ in range(R): 9 li = list(str(input())) 10 li = [9 if el == '#' else 0 for el in li] 11 maze.append(li) 12 13maze[gy - 1][gx - 1] = 1 14 15dic = {} 16for i in range(R - 1): 17 for j in range(C - 1): 18 dic[i, j] = 0 19 20data = deque([[sy - 1, sx - 1, 0]]) 21while data[0]: 22 x, y, d = data.popleft() 23 24 if maze[x][y] == 1: 25 print(d) 26 break 27 maze[x][y] = 2 28 29 if maze[x - 1][y] < 2: 30 if dic[x - 1, y] < 1: 31 data.append([x - 1, y, d + 1]) 32 dic[x - 1, y] += 1 33 if maze[x + 1][y] < 2: 34 if dic[x + 1, y] < 1: 35 data.append([x + 1, y, d + 1]) 36 dic[x + 1, y] += 1 37 if maze[x][y - 1] < 2: 38 if dic[x, y - 1] < 1: 39 data.append([x, y - 1, d + 1]) 40 dic[x, y - 1] += 1 41 if maze[x][y + 1] < 2: 42 if dic[x, y + 1] < 1: 43 data.append([x, y + 1, d + 1]) 44 dic[x, y + 1] += 1

解法②

Python

1from collections import deque 2 3R, C = map(int, input().split()) 4sy, sx = map(int, input().split()) 5gy, gx = map(int, input().split()) 6 7maze = [] 8for _ in range(R): 9 li = list(str(input())) 10 li = [9 if el == '#' else 0 for el in li] 11 maze.append(li) 12 13maze[sy - 1][sx - 1] = 2 14maze[gy - 1][gx - 1] = 1 15 16data = deque([[sy - 1, sx - 1, 0]]) 17while data[0]: 18 x, y, d = data.popleft() 19 20 if x == gy - 1 and y == gx - 1: 21 print(d) 22 break 23 24 if maze[x - 1][y] < 2: 25 data.append([x - 1, y, d + 1]) 26 maze[x - 1][y] = 2 27 if maze[x + 1][y] < 2: 28 data.append([x + 1, y, d + 1]) 29 maze[x + 1][y] = 2 30 if maze[x][y - 1] < 2: 31 data.append([x, y - 1, d + 1]) 32 maze[x][y - 1] = 2 33 if maze[x][y + 1] < 2: 34 data.append([x, y + 1, d + 1]) 35 maze[x][y + 1] = 2

解法③

Python

1from collections import deque 2 3R, C = map(int, input().split()) 4sy, sx = map(int, input().split()) 5gy, gx = map(int, input().split()) 6 7maze = [] 8for _ in range(R): 9 li = list(str(input())) 10 li = [9 if el == '#' else 0 for el in li] 11 maze.append(li) 12 13maze[gy - 1][gx - 1] = 1 14 15data = deque([[sy - 1, sx - 1, 0]]) 16while data[0]: 17 x, y, d = data.popleft() 18 19 if maze[x][y] == 1: 20 print(d) 21 exit() 22 23 if maze[x][y] >= 2: 24 continue 25 26 maze[x][y] = 2 27 28 if maze[x - 1][y] < 2: 29 data.append([x - 1, y, d + 1]) 30 if maze[x + 1][y] < 2: 31 data.append([x + 1, y, d + 1]) 32 if maze[x][y - 1] < 2: 33 data.append([x, y - 1, d + 1]) 34 if maze[x][y + 1] < 2: 35 data.append([x, y + 1, d + 1]) 36

投稿2021/07/12 05:20

編集2021/07/12 08:15
kay_ventris4

総合スコア269

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問