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

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

新規登録して質問してみよう
ただいま回答率
85.48%
多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

1459閲覧

2次元配列を使った迷路探索(深さ優先探索)

kdt.nmk

総合スコア20

多次元配列

1次元配列内にさらに配列を格納している配列を、多次元配列と呼びます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2022/01/27 01:18

[A - 深さ優先探索] https://atcoder.jp/contests/atc001/tasks/dfs_a

 この問題を解きたいと思い、下記のコードを書きました。
現在位置の上には進めるのですが、それ以外の方向に思ったように進みません。
配列の範囲についてエラーが出ているので、それと関係あると思うのですが、根本原因が分かりません。
デバッグをして、if文のあたりが怪しいような気がしています。
深さ優先探索の仕組みについては、概ね理解しているつもりです。
よろしくお願いします。

c++

1#include <bits/stdc++.h> 2using namespace std; 3 4bool find (int H, int W, vector<vector<char>> map, vector<vector<int>> stack, int a, int b) 5{ 6//現在地がgになったらtrue、終了 7 if(map.at(a).at(b) == 'g') 8 { 9 cout<<"goal"<<endl; 10 return true; 11 } 12 13 stack.push_back({a,b}); 14 map.at(a).at(b)='o'; 15 cout<<endl; 16 cout<<"------------------"<<endl; 17 cout << a<<","<<b<<endl; 18 for (int i = 0; i < H; ++i) 19 { 20 for (int j = 0; j < W; ++j) 21 { 22 cout<< map.at(i).at(j); 23 } 24 cout << endl; 25 } 26 27 28//進めるなら進む 29 if((map.at(a-1).at(b)=='.' || map.at(a-1).at(b)=='g') && 0 <= a-1 && a-1 < H && 0 <= b && b < W) 30 { 31 cout << "上"<< endl; 32 a-=1; 33 return find (H, W, map, stack, a, b); 34 } 35 else if ((map.at(a+1).at(b)=='.' || map.at(a+1).at(b)=='g') && 0 <= a+1 && a+1 < H && 0 <= b && b < W) 36 { 37 cout << map.at(a+1).at(b) <<endl; 38 cout << "下"<< endl; 39 a+=1; 40 find (H, W, map, stack, a, b); 41 } 42 else if ((map.at(a).at(b-1)=='.' || map.at(a).at(b-1)=='g') && 0 <= a && a < H && 0 <= b-1 && b-1 < W) 43 { 44 cout << "左"<<endl; 45 b-=1; 46 find (H, W, map, stack, a, b); 47 } 48 else if ((map.at(a).at(b+1)=='.' || map.at(a).at(b+1)=='g') && 0 <= a && a < H && 0 <= b+1 && b+1 < W) 49 { 50 cout << "右"<<endl; 51 b+=1; 52 find (H, W, map, stack, a, b); 53 } 54//四方行けなかったら、道が出てくるまで1マスずつ戻る 55 else 56 { 57 cout << "行き止まり"<<endl; 58 while (map.at(a-1).at(b)=='.' || map.at(a+1).at(b)=='.' || map.at(a).at(b-1)=='.' || map.at(a).at(b+1)=='.' || map.at(a-1).at(b)=='g' || map.at(a+1).at(b)=='g' || map.at(a).at(b-1)=='g' || map.at(a).at(b+1)=='g') 59 { 60 int i = 2; 61 a=stack.at(stack.size()-i).at(0); 62 b=stack.at(stack.size()-i).at(1); 63 i++; 64 //スタートを通過しても行ける場所が無かったら終了 65 if (i>stack.size()+10) 66 { 67 return false; 68 } 69 find (H, W, map, stack, a, b); 70 } 71 } 72} 73 74 75 76int main() 77{ 78 79 int H, W; 80 cin >> H >> W; 81 82//迷路入力 83 vector<vector<char>> map(H, vector<char>(W)); 84 for (int i = 0; i < H; ++i) 85 { 86 for (int j = 0; j < W; ++j) 87 { 88 cin >> map.at(i).at(j); 89 } 90 } 91 92//スタート地点確認 93 int a, b; 94 for (int i = 0; i < H; ++i) 95 { 96 for (int j = 0; j < W; ++j) 97 { 98 if (map.at(i).at(j) == 's') 99 { 100 a=i; 101 b=j; 102 } 103 } 104 } 105 106 vector<vector<int>> stack(0, vector<int>(0)); 107 108 bool result = find(H,W,map,stack,a,b); 109 cout << result << endl; 110} 111

input

14 3 2s.. 3.## 4... 5g.. 6
エラーメッセージ

[Wandbox] Start
prog.cc: In function 'bool find(int, int, std::__debug::vector<std::__debug::vector<char> >, std::__debug::vector<std::__debug::vector<int> >, int, int)':
prog.cc:72:1: warning: control reaches end of non-void function [-Wreturn-type]
72 | }
| ^
引用テキスト
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 4) >= this->size() (which is 4)
[Signal] Aborted
[Wandbox] Finish

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

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

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

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

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

guest

回答1

0

ベストアンサー

if((map.at(a-1).at(b)=='.' || map.at(a-1).at(b)=='g') && 0 <= a-1 && a-1 < H && 0 <= b && b < W)

これ系の箇所,
ショートサーキットを想定しているのであれば,範囲チェックを先に書く必要があります.

C++

1if( 2 0 <= a-1 && a-1 < H && 0 <= b && b < W //範囲チェックを先に 3 && (map.at(a-1).at(b)=='.' || map.at(a-1).at(b)=='g') //mapへのアクセスを後に 4)

[追記]
それはそれとして,
find() は再帰として実装しつつも stack とかいう謎の履歴データ(?)っぽいのがあったりして,考えが整理されてない感.
再帰でやるならこのくらい↓の話になるのでは?

C++

1bool find( int H, int W, std::vector< std::vector<char> > &map, int x, int y ) 2{ 3 if( x<0 || y<0 || W<=x || H<=y )return false; 4 5 char &attr = map[y][x]; 6 if( attr=='#' || attr=='o' )return false; 7 8 std::cout << x << ", " << y << std::endl; //output for debug 9 if( attr=='g' )return true; 10 attr = 'o'; 11 12 return ( find(H,W,map,x,y-1) || find(H,W,map,x-1,y) || find(H,W,map,x+1,y) || find(H,W,map,x,y+1) ); 13} 14 15int main() 16{ 17 //※入力部とかは面倒なので省略※ 18 std::vector< std::vector<char> > map 19 { 20 { 's', '.', '.' }, 21 { '.', '#', '#' }, 22 { '.', '.', '.' }, 23 { 'g', '.', '.' } 24 }; 25 std::cout << ( find( 4,3,map,0,0 ) ? "Yes" : "No" ) << std::endl; 26 return 0; 27}

投稿2022/01/27 01:39

編集2022/01/27 04:33
fana

総合スコア11658

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

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

kdt.nmk

2022/01/27 11:05

質問への回答、及び追記ありがとうございます。 追記について、returnの仕方に膝を打ちました。 returnで演算子を使える事を知らず(そしてそのような発想が全く無く)、冗長なコードになっていました。 return で or を使って、「4方向の内、壁が無い方向(true)を返す」 + 「それを総当たりで繰り返して、最終的にtrueになる組み合わせを見つけてくる」という理解で正しいでしょうか。
kdt.nmk

2022/01/27 11:15

(記録として書かせていただきます。) 質問で上げたコードは、if を使って、わざわざ進む方向を1マス1マス決定しようとする考え方をしています。 そうすると、「進んだ先が袋小路になっている → 来た道を戻る」という手順が必要になるため、stack で道順を記録し、それを使って、来た道を分岐点まで戻るという操作をしようとしたため、変数に道順の履歴を入れるための配列が入っています。 総当たりで判断できるのであれば、そのような手順は必要ないです。
fana

2022/01/28 03:02

> return ( find(H,W,map,x,y-1) || find(H,W,map,x-1,y) || find(H,W,map,x+1,y) || find(H,W,map,x,y+1) ); は,単に if( find(H,W,map,x,y-1) )return true; if( find(H,W,map,x-1,y) )return true; if( find(H,W,map,x+1,y) )return true; if( find(H,W,map,x,y+1) )return true; return false; を1行で書いたというだけです.
fana

2022/01/28 03:07

> 来た道を分岐点まで戻るという操作 再帰的に呼びされている find() というのは,「1個手前のマスを調べた find()」 から呼ばれているわけですから, 来た道を1個戻るには単に return false; とすれば良いです. 「現在までの呼び出しの羅列=履歴」なわけですから別途履歴を保持する必要はありません.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問