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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++

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

Q&A

解決済

1回答

576閲覧

深さ優先検索疑問点について

jaogjig

総合スコア21

C++

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

0グッド

0クリップ

投稿2022/08/31 10:56

前提

入力として Nは頂点数6M辺数7とする
A[100009]配列
B[100009]配列
この二つは隣接しているリストとする
「vector<int> G[100009];」のGは頂点の番号を表している。
このようにA[i]B[i]に以下のように入力例とする
G1:{2,3}
G2:{1,4,5}
G3{1,4}
G4{2,3,6}
G5{2,6}
G6{5,4}
2列3行の四角形になります。

bool visited[100009]; visited[pos]=false のとき頂点 x が白色、true のとき灰色
とします。

手順としては

『手順1.全ての頂点を白色で塗る
手順2.一番最初に頂点1を訪問し、頂点1を灰色で塗る
手順3.その後、以下の操作を繰り返す。頂点1で以下のaが当てはまったら検索終了。
a. 隣接する頂点がすべて灰色である場合、一歩だけ戻る。
b. そうでは無い場合、隣接する白色頂点のうち番号が最小の頂点を訪問する。新たに頂点を訪問する際には、その頂点を灰色で塗る。
手順4.最終的にすべての頂点が灰色で塗られた場合、グラフは連結する。』
引用:問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本 単行本(ソフトカバー) – 2021/12/25
米田 優峻 (著)

出力としてはこのグラフが連結であるかどうかを求めるプログラムです

発生している問題

void dfs(int pos) { visited[pos] = true; // for (int i : G[pos]) のような書き方を「範囲 for 文」といいます。(APG4b 2.01 節) for (int i : G[pos]) { if (visited[i] == false) dfs(i); } }

この部分で
最初にint pos で定義したのにvisited[pos] = true;とposがbool型になるのは問題ないのですか

void dfs(int pos) { visited[pos] = true;// 手順2.一番最初に頂点1を訪問し、頂点1を灰色で塗る // for (int i : G[pos]) のような書き方を「範囲 for 文」といいます。(APG4b 2.01 節) for (int i : G[pos]) { if (visited[i] == false) dfs(i); } }

上記の部分は手順で言うと下記と対応していると思います。
『手順2.一番最初に頂点1を訪問し、頂点1を灰色で塗るvisited[pos] = true;このプログラムと対応している。
手順3.その後、以下の操作を繰り返す。頂点1で以下のaが当てはまったら検索終了。
a. 隣接する頂点がすべて灰色である場合、一歩だけ戻る。
b. そうでは無い場合、隣接する白色頂点のうち番号が最小の頂点を訪問する。新たに頂点を訪問する際には、その頂点を灰色で塗る。』

for (int i : G[pos]) { if (visited[i] == false) dfs(i); } }

この上記のプログラムでは
手順3.その後、以下の操作を繰り返す。頂点1で以下のaが当てはまったら検索終了。
a. 隣接する頂点がすべて灰色である場合、一歩だけ戻る。
b. そうでは無い場合、隣接する白色頂点のうち番号が最小の頂点を訪問する。新たに頂点を訪問する際には、その頂点を灰色で塗る。
と対応していると思いますが
隣接する頂点がすべて灰色である場合、一歩だけ戻るとプログラムで記述されているようには思えません。
また、最小の頂点を訪問しているとプログラムで記述されているようには思えません。どのように動いているのかご教授お願いできませんか

該当のソースコード

#include <iostream> #include <vector> #include <algorithm> using namespace std; int N, M, A[100009], B[100009]; vector<int> G[100009]; bool visited[100009]; // visited[pos]=false のとき頂点 x が白色、true のとき灰色 void dfs(int pos) { visited[pos] = true; // for (int i : G[pos]) のような書き方を「範囲 for 文」といいます。(APG4b 2.01 節) for (int i : G[pos]) { if (visited[i] == false) dfs(i); } } int main() { // 入力 cin >> N >> M; for (int i = 1; i <= M; i++) { cin >> A[i] >> B[i]; G[A[i]].push_back(B[i]); G[B[i]].push_back(A[i]); } // 深さ優先探索 dfs(1); // 連結かどうかの判定(Answer=true のとき連結) bool Answer = true; for (int i = 1; i <= N; i++) { if (visited[i] == false) Answer = false; } if (Answer == true) cout << "The graph is connected." << endl; else cout << "The graph is not connected." << endl; return 0; }

補足情報(FW/ツールのバージョンなど)

問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本 単行本(ソフトカバー) – 2021/12/25
米田 優峻 (著)
4.5.6深さ優先検索P170〜172

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

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

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

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

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

Crimson_Tide

2022/08/31 22:17

[そうでは無い場合、隣接する白色頂点のうち番号が最小の頂点を訪問する。]には注記があります。 >注 4.5.4 ここでは説明の都合上このようにしていますが、番号が最小の頂点でなくても、 隣接する白色頂点の中から適当に1つ選べばアルゴリズムは正しく動作します。 引用元:米田 優峻. 問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本 (p.352). 株式会社技術評論社. Kindle 版. 入力データ次第で最小の頂点を訪問するようになるだけの話なので、「最小の頂点を訪問する処理になっている」かどうかを考える必要はありません。
fana

2022/09/01 01:01

> この部分で > 最初にint pos で定義したのにvisited[pos] = true;とposがbool型になるのは問題ないのですか ↑この文章の意味が全くわからないのですが.
Crimson_Tide

2022/09/01 02:51

一連の質問者さんの質問を見る限り基本的なプログラミング、C++の基礎知識がないように見受けられます。 visited[pos] = trueが posにtrueが代入されて型が変わっているように見える、とかかと想像。 例えるなら算数すっ飛ばして中学数学に取り組もうとしている小学生が、習ったことのない掛け算を勝手に解釈しているようなものかと・・・ 1. 3. 3   前提 と なる 知識 また、 本書 では いくつ かの ソース コード を 掲載 し て いる ため、 プログラミング に 触れ た こと が あり、 1 つ 以上 の プログラミング 言語 で 基礎的 な 文法 を 習得 し て いる こと が 望ましい です。 米田 優峻. 問題解決のための「アルゴリズム×数学」が基礎からしっかり身につく本 (p.25). 株式会社技術評論社. Kindle 版. 質問者さんはまずC++の基礎を学習されるべきです。
jaogjig

2022/09/01 08:17

了解しました。C++の基礎をやってみます。
jaogjig

2022/09/05 10:06

基礎学習をしました。 visited[pos] はデータ配列の空の変数が入っているだけで = trueが posにtrueが代入されて型が変わっているように見える、 のではなくて bool visit[ここの数字が整数の(posのint)型1]{0,1,2,3...100008} 1番目にtrueを入れているだけである。 ってことで合っていますか 自分はintとboolの型が外の配列をbool visit[1]と決めたら変えられないと思っていました。 中の数字にもデータ型を決められることが分かりませんでした。
guest

回答1

0

ベストアンサー

動作のイメージ確認用にcoutによる状態出力を追加してみました。

c++

1#include <iostream> 2#include <vector> 3#include <algorithm> 4using namespace std; 5 6int N, M, A[100009], B[100009]; 7vector<int> G[100009]; 8bool visited[100009]; // visited[pos]=false のとき頂点 x が白色、true のとき灰色 9 10void dfs(int pos) { 11 cout << "starting dfs(" << pos << ")" << endl; 12 visited[pos] = true; 13 // for (int i : G[pos]) のような書き方を「範囲 for 文」といいます。(APG4b 2.01 節) 14 for (int i : G[pos]) { 15 cout << "in dfs(" << pos << ") for loop" << endl; 16 cout << " i = " << i << endl; 17 if (visited[i] == false) dfs(i); 18 } 19 cout << "exiting dfs(" << pos << ")" << endl; 20} 21 22int main() { 23 // 入力 24 cin >> N >> M; 25 for (int i = 1; i <= M; i++) { 26 cin >> A[i] >> B[i]; 27 G[A[i]].push_back(B[i]); 28 G[B[i]].push_back(A[i]); 29 } 30 31 // 深さ優先探索 32 dfs(1); 33 34 // 連結かどうかの判定(Answer=true のとき連結) 35 bool Answer = true; 36 for (int i = 1; i <= N; i++) { 37 if (visited[i] == false) Answer = false; 38 } 39 if (Answer == true) cout << "The graph is connected." << endl; 40 else cout << "The graph is not connected." << endl; 41 return 0; 42}

この状態でプログラムを実行すると以下のようなログになります。
ログを読んでいくと以下のようなことが分かります

  • dfs(6)が呼び出されるのは、dfs(5)のforループの中から
  • dfs(6)内で6に隣接するノード5, 4をチェックするが、訪問済(visited[i] == true)なので、ここからdfs(5), dfs(4)を呼び出すことはない
  • dfs(6)終了後、dfs(5)のforループに戻って実行を続けている

text

1// グラフ情報の入力 26 7 31 3 41 2 55 6 62 4 72 5 83 4 94 6 10 11// プログラムからの出力 12starting dfs(1) 13in dfs(1) for loop 14 i = 3 15starting dfs(3) 16in dfs(3) for loop 17 i = 1 18in dfs(3) for loop 19 i = 4 20starting dfs(4) 21in dfs(4) for loop 22 i = 2 23starting dfs(2) 24in dfs(2) for loop 25 i = 1 26in dfs(2) for loop 27 i = 4 28in dfs(2) for loop 29 i = 5 30starting dfs(5) 31in dfs(5) for loop 32 i = 6 33starting dfs(6) 34in dfs(6) for loop 35 i = 5 36in dfs(6) for loop 37 i = 4 38exiting dfs(6) 39in dfs(5) for loop 40 i = 2 41exiting dfs(5) 42exiting dfs(2) 43in dfs(4) for loop 44 i = 3 45in dfs(4) for loop 46 i = 6 47exiting dfs(4) 48exiting dfs(3) 49in dfs(1) for loop 50 i = 2 51exiting dfs(1) 52The graph is connected.

ご質問にある本は持っていないのですが、a, bの説明を以下のように少し変更して読んでみてはいかがでしょうか。

a. 隣接する頂点がすべて灰色である場合、(dfs関数を抜けることで)一歩だけ戻る。
b. そうでは無い場合、現在のノードposに隣接する頂点(G[pos])を順番に見ていき、最初に白色になっている頂点を訪問する(dfs(i)を呼び出すことで一歩進む)。新たに頂点を訪問する際には、その頂点を灰色で塗る。

これで、下記のようなグラフ情報が与えられたとします。

text

16 7 21 3 31 2 45 6 52 4 62 5 73 4 84 6

この場合、dfs(1) -> dfs(3) -> dfs(4) -> dfs(2) -> dfs(5) -> dfs(6) と次々にdfs()関数を呼び出して一歩ずつ順調に進んでいきます。しかし、dfs(6)まできたところで「隣接する頂点が全て灰色」になります。
次のdfs()関数を呼び出すことができないので、そのままforループを抜けてdfs(6)を終了し、一歩戻ってdfs(5)関数に返ってきます。
dfs(5)でもaの条件が発生するので一歩戻ってdfs(2)に返ります。
同じことが続いて dfs(4) -> dfs(3) -> dfs(1) と一歩ずつ戻ってきて、最終的にmain()へ戻ります。

投稿2022/08/31 12:59

編集2022/09/05 11:35
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

jaogjig

2022/09/05 10:49 編集

回答ありがとうございます。 <そのままforループを抜けてdfs(6)を終了し、一歩戻ってdfs(5)関数に返ってきます。 dfs関数を抜けることでと書いてありますがなぜdfs関数を抜けると一歩戻ってdfs関数がまた使われるのかが分かりません。
退会済みユーザー

退会済みユーザー

2022/09/05 11:36

長くなるので解答にサンプルコードを追記しました。
jaogjig

2022/09/05 12:36

回答ありがとうございます。 どうやって動いているのかはすごく分かりやすくなりました。 <dfs(6)終了後、dfs(5)のforループに戻って実行を続けている と書いてありますが、何が働いてdfs(5)のforループに戻って実行を続けているのですか 自分はdfs(6)終了後、”void dfs(int pos) {”の部分が働いてdfs(5)のforループに戻って実行を続けていると思いましたが違うようでした。
退会済みユーザー

退会済みユーザー

2022/09/05 13:29

「何が働いて」というのが、どのような粒度での答えを想定しているのか不明ですが、 たとえば現在のプログラムでは、main関数からdfs(1)を呼びだすと、main関数での処理を一旦中断してdfs(1)内でいろいろな処理を行ないますよね。このdfs(1)での処理が終了した後、main関数に戻ってきて残りの処理を続けてグラフが連結かどうかを表示します。 これと同じで dfs(5)からdfs(6)を呼び出すと、dfs(5)のforループ内での処理を一旦中断してdfs(6)でいろいろな処理を行ないます。dfs(6)が終了すると、dfs(5)に戻ってきてdfs(6)を呼び出した直後の部分から処理を再開します。
jaogjig

2022/09/05 14:48

回答ありがとうございます。 dfs(1) -> dfs(3) -> dfs(4) -> dfs(2) -> dfs(5) -> dfs(6) と続いて各関数がループの途中だったからdfs(1) <- dfs(3) <-dfs(4) <-dfs(2) <-dfs(5)と処理が続行するということですか
退会済みユーザー

退会済みユーザー

2022/09/05 15:18

そうですね。ログ出力のstarting... や exiting...を読むとどのような順番で開始/終了したか分かります。グラフの頂点や辺の設定をいろいろと変えて納得いくまで動かしてみると良いと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問