• ベストアンサー

すべての木のパス(経路)を求めたいのですが

こんにちは。学校の課題のことで質問させてもらいます。 例えばAはB1,B2,B3,B4に接続されています。B1はC1,C2へ,B2はC3,C4へ,B3はC5,C6へ,B4はC7,C8へそれぞれ接続されています。このような木の経路は8種類で A→B1→C1 A→B1→C2    … A→B4→C7 A→B4→C8 ですよね?これですべての経路を画面に出力するようなプログラムを考えなければならないのですが、どのようにすすめていいか分からないです。AからBなどの接続はポインタによって接続されています。 コンピュータにポインタを辿らせると最初のA→B1→C1の経路しか選択しないので一度通った場所(ノード?)には通らないように目印を付けなければならないと思うのですが、その条件がよくわからないです。 またこのようなプログラムはfor文やwhile文を使うのではなく再帰呼び出しを使うのでしょうか? 補足質問もわかる範囲ならば答えたいと思います。 回答よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • noocyte
  • ベストアンサー率58% (171/291)
回答No.3

始点を S1,…,S6,終点を E1,E2 として,仮に S1 と E1 を選んだとしましょう. >(終点が2つなので)自分が選択した終点に辿り着いたものだけを > 表示させるためにはどうしたらよいのでしょうか? 現在の経路を表示させるか否かを判断できるのは,E1 または E2 に到達した時点です. それまで,現在の経路を覚えておく必要があります. (単純に,途中のノードを訪れるたびにその名前を出力していたのでは, 不要な経路も出力されてしまいます.) 経路を N0(=S1) → N1 → N2 → … → Nm(=E1 または E2) とします. 多分この課題ではすべての経路についてのmの最大値 (つまり最長経路の長さ) はわかっているでしょうから,max(m)+1 個以上の要素を持つ,ノードへの ポインタ配列を用意して,そこに現在の経路を記憶させるのが最も簡単でしょう. S1 を出発点として #1 のトラバース (渡り歩く) プログラムを使って, E1 に到達するたびに配列に記憶させた経路を出力すれば OK です. (E2 に到達したときは何もしない.) もちろん,#1 のプログラムを少し拡張して,上記配列の添字 (=出発点 S1 から現在位置までの距離) を管理するようにしなければなりません. あと,各ノードの値の合計も. # ●余談 # 実は配列を使わなくても,現在の経路は再帰呼び出しのスタックが覚えており, # そこから情報を取り出すという手もあるのですが,少しプログラムがわかりにくく # なるのでやめときます:) # でも,自分ではこっちの手を使います:) ●参考 以下のキーワードで検索してみるといいかもしれません. ・"アサイクリックグラフ" (acyclic graph) または "有向非環状グラフ" (directed acyclic graph,"DAG") (ループのない有向グラフ) ・"深さ優先探索" (depth-first search) または "縦型探索" (#1 のようなアルゴリズムを一般化したもの.) ・"グラフのトラバース" (グラフのすべてのノードを順番に渡り歩くこと)

kevin23
質問者

お礼

回答ありがとうございます!! その経路が求めたい経路かどうかを判断できるのは終点に達した時点だから、通過した場所を記憶するものを用意しておけばよかったんですね。 自分でもどのようなことをすれば良いかイメージができてきました。 試行錯誤してプログラムを作ってみたいと思います! 参考になりました!!

その他の回答 (2)

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.2

私はグラフアルゴリズムが好きなので口出ししちゃいます.(笑) でも,正解はすでに #1 さんが書いておられるとおりだし, 課題ということなのであまり詳しくは書けないので補足程度に. > またこのようなプログラムはfor文やwhile文を使うのではなく > 再帰呼び出しを使うのでしょうか? #1 さんの回答にあるとおり, ・経路を先に進むには再帰呼び出しを使う. ・枝分かれした道をすべて列挙するには for または while を使う. > 一度通った場所(ノード?)には通らないように目印を > 付けなければならないと思うのですが、 この課題は経路にループがない木なので,その必要はありません. # ●余談 (読み飛ばしてもかまいません.) # ループが存在しなくても合流点が存在し,かつそれ以後の経路を # 重複して列挙しないという課題であれば目印をつける必要があります. # しかしこの課題は「すべての経路を列挙せよ」ということなので, # 仮に合流点が存在していたとしても目印は不要です.

kevin23
質問者

お礼

回答ありがとうございました! 参考になりました!!

kevin23
質問者

補足

回答ありがとうございます!! 再帰呼び出しを使うことはわかりました。ただ実はこの課題の木はこのように単純ではなくて、始点が6つで終点が2つあります。自分が選択した始点と終点を結ぶ複数の経路を列挙しなければならないのです。 質問事項に書いていなくてすいません。   さらに通るノードが所持している値を加算して、その複数の経路の中で最も加算した値が小さいものの値と経路を出力しなければなりません。(これは多分自分でもできそうです) この場合も再帰呼び出しですべての経路を求めることは出来ると思いますが、ゴールが2つあるので自分が求めたい経路を辿らないかもしれません。(終点が2つなので)自分が選択した終点に辿り着いたものだけを表示させるためにはどうしたらよいのでしょうか?printf文を再帰呼び出しの中に書くと不要な経路まで出力されそうです。 長文になり申し訳ありません。お暇であればお答えいただくとありがたいです。 回答参考になりました!!

回答No.1

ループのない"木"であれば、 void 渡り歩く(ノード x) {  for ( すべての x の子: child に対し ) {   渡り歩く(child);  } } ↑これを根に対して適用すればいいでしょう。

kevin23
質問者

お礼

回答ありがとうございます!! 再帰呼び出しを使うのですね。 プログラムを書いてみたいと思います。 参考になりました!!

関連するQ&A

  • 再帰呼び出しで求めたい経路を表示させたい!

    以前も「すべての経路を求めるには」というような質問をして再帰呼び出しにより無事解決することが出来ました。 しかし、少しわからないところがあります。 例えばaはb1とb2に接続されていてb1とb2はそれぞれc1,c2とc3,c4に接続されているとします。すべての経路は全部で4本ですね。 これを再帰呼び出しの中にprintf文を用いて表示させたところ a->b1->c1 c2 b2->c3 c4 というような感じで表示されたと思います。これは再帰呼び出しの性質で分岐があった以前の場所へ戻って探索を続けるからだというのは理解できました。しかし、実際は a->b1->c1 a->b1->c2 a->b2->c3 a->b2->c4 というように表示させたいです。何かこれを記憶するための変数が必要だと思うのですが、それを用いて上のように表示させるほう方法が思いつきません。 さらに、a->b2->c3というように特定の経路だけを選択して表示させたいです。アドバイスよろしくお願いします!

  • C:経路検索アルゴリズム

    全経路検索について勉強中です。 「各ノードはポインタで繋がっている」という事は理解しましたが、それをどのように表せば良いのでしょう。 ダイクストラ法、深さ優先検索等の単語は見つかりましたが、どれを使用すれば良いのか…。 条件は、ノードの数は決まっておらず、つながり・出発,到着点は自由に決める事が出来ます。  例)1-2 1-4 2-4 2-3 3-5 4-5 開始,3 到着,5 距離は関係ありません。 今はリスト構造で、構造体(下のようにしています)は動的に確保し、 再帰で処理をするようにしていますが、上手くいかず不安になってきました。 struct data{   int x;       /*ノード*/   struct data *next; /*次ノードへのつながり*/ }; よろしければ、何か些細な事でもヒントを下さると嬉しいです^^

  • C言語のプログラムについて

    C言語のプログラムについて 3桁の自然数の中で、自分自身を含めた約数が奇数になるものがいくつあるかを求めるプログラムを作りたいのですが、swich文を使って、6通りの方法で出そうとしていまして、 while 文、 for文、 do while文に加え、 for文のを、1つの関数として独立させたもの、 さらに、for文のを重ループ部分のそれぞれのループに対応して、2つの関数として独立させたもの、 そして、この2つの関数のどちらともをループを用いずに再帰呼び出しを用いたもの の6通りで出したいのですが、swich文を使うところは自力でできたのですが、あとの6つそれぞれのプログラムの組み方がわかりません。 教えていただけないでしょうか?ややこしい書き方をしてすいません・・・。

  • メバロン酸経路と非メバロン酸経路

    天然物化学についての質問です。 大学の授業でモノテルペン(C10)、ジテルペン(C10)、 セスタテルペン(C25)、カロチノイド(C40)は「非メバロン酸経路」で、 セスキテルペン(C15)、スクワレンやトリテルペン(C30)や ステロイドは「メバロン酸経路」と習いました。 メバロン酸経路と非メバロン酸経路を見分けるのは このように炭素の数で見分ければいいのでしょうか? その後、モノテルペンインドールアルカロイドは モノテルペンなのにも関わらず、シキミ酸経路と メバロン酸経路(非メバロン酸経路ではなくです。) と授業でやり混乱しております。 どなたか、テルペノイドがメバロン酸経路と非メバロン酸経路の どちらからできているか見分ける方法をご存じの方がいましたら、 回答をしてはいただけないでしょうか。 また、この教授とコミュニケーションをとることは 限りなく無理に等しいため、教授に質問に行くべきという回答は 申し訳ありませんが、やめてください。 よろしくお願い致します。

  • VC++ 再帰呼び出しについて

    VC++6.0にてプログラミングを行っているものですが、 関数の再帰呼び出しについて質問です。 再帰呼び出しの際にスタックに積まれる変数というのは、 再帰呼び出しをする関数に渡す引数のことですか? スタックオーバーフローを起こさないために、 staticなポインタにHeap領域上の 変数を割り当てるとよい。 と分かったのですが、 この意味は、例えば static int *a = new int; ということなのですか?

  • pascalにおけるwhile文の条件について

    「cがA,B,Cのどれかの場合はwhile文の内容を繰り返す」というプログラムは while c in ['A','B','C'] do begin ~ end; と書けますが、「cがA,B,Cのどれかで無い場合はwhile文の内容を繰り返す」というプログラムはどのように書けばよいのでしょうか。私は while not c in ['A','B','C'] do begin ~ end; だと思ったのですが、エラーが発生したのでどうやら違うようです。 わかる人がいれば教えてください。よろしくお願いします。

  • ネットワークの経路について、

    ネットワークの経路について、 拠点Aと拠点B間はWANとVPNの2回線で接続されています。 Bにある特定のサーバへのルートをWANにする設定をAのルータにした場合、BへのリクエストはWANですが、レスポンスはどっちの経路になるのでしょうか? 初歩的な質問ですがお願いします。

  • 左再帰について

    次のBNFが左再帰であるかないかを判定し、その根拠を説明せよ。 <A> ::=<B>|<C>|<C> <B> ::=if<C>then<A> <C> ::=a|b|c <D> ::=<A>while<C> これを簡単でいいので教えてください。 できれば左再帰とは何かも教えてください。

  • TreeViewコントロールについて

    いつも参考にさせて頂いてます。 早速ですが、DBの値をTreeViewを使用して表示したいと思っています。 DBには、 CD   機能1  機能2  機能3 CD1   A1   B1    C1 CD1   A1   B2    C2 CD1   A1   B2    C3 CD2   A2   B3    C4 見たいな感じでデータが登録されています。 これをTreeViewに、 CD1  LA1    LB1    | LC1    LB2      LC2      LC3 CD2  LA2    LB3      LC4 と出力させたいのですが、どうしたらよいか見当もつきません。 いろいろ調べて再帰呼び出しで出来るんじゃないかと思うのですが、 再帰処理にに詳しくない為、先に進むことが出来ません。 どうしたら実現できるか、力をお貸しください。 宜しくお願い致します。

  • 空間の最短経路

    立方体4つをくっつけ、上から眺めたときに左下に立方体を1つくっつけた図形がある 左下の立方体の左下の角をA、初めの4つの立方体のうちの右上の立方体の右上の角をC、右下の角をBとする(つまりBはAから右に3つ、そこから前に2つ行けばたどり着ける位置、CはBの真上の角) このときAからBへの最短経路は何通りか またAからCへの最短経路は何通りか AからBへはAの右から行くか前から行くかで場合分けして、 右に行く場合は4C2 前に行く場合は一つ右に行き3C1 6+3=9通りと出たのですがAからCへの最短経路が分かりません 解き方を教えてください

専門家に質問してみよう