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

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

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

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

Q&A

4回答

695閲覧

チェスのナイトが出発地点から目標地点まで移動する時のルートを保存、出力したい。BFS Knight's Tour

kalmia

総合スコア15

C++

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

0グッド

0クリップ

投稿2022/06/18 17:32

knight's tourという問題で、queueを利用したBFSで作っています。
基盤となるボードの大きさは8x8で、出発点は(0, 0)、目標地点は(6, 7)です。
問題は
1、最短何ステップで到着するかint型で出力。
2、その際通った道を1、2、3、、、と表示する。(参考写真あり)
このようになっています。これの2番で悩んでいます。

大幅な進行を説明します。
盤は2つあり、訪問したマスをマークする(何回目で訪れたかを数字で保存してある)ものと、出力用のものです。2次元arrayで作られています。(int path 出力用、一つの道のみ表示し、他は0にしたい)(int visited)

Moveは別で宣言されているstructで、x座標とy座表を保存できます。(2, 3)このように。
firは初期地点(0、0)がはいっており、destには目標地点(6、7)が保存されています。
*moveはナイトが動くことのできる8種類の座標移動が入っており、(move[0] = {+2, +1},,,)これを一つずつ足すことによって探索をします。
visitedには訪問済みのvertixのマークおよび何回でそのvertixに行けたのかを保存しています。

**今、下のコードではvisitedを丸ごとpathに移していますが、これを最適な道だけをpathに保存したいです。
**
しかし、ずっと考えているのですがアイデアが浮かびません。
ソースコードを全て載せたいのですが、コピー検査もあるはずなので載せれませんでした。
なにかアイデアをいただきたいです。

int shortdistance(Move fir, Move dest, int path[b_size][b_size], int nrMove, Move *move) { int q_limit= pow(nrMove, b_size); struct Node{ int distance; //何回目で到着したかをカウント Move position; //現在位置を表す変数 }; Queue<Node> q(q_limit); int visited[b_size][b_size] = {0, };  //すべてのvertixを未訪問に設定 visited[0][0] = 1; //初期位置を訪問済みにする Node n; n.distance = 0; //何回動いたかカウントする n.position = fir; //現在いる位置を表す q.enqueue(n); while(!q.isEmpty()){ auto v = q.dequeue(); //初期位置を設定 for(int i = 0; i < 8; i++)}  //ナイトの動き方が8種類ある if(v.position == dest){ //目標地点に到着 > その時のdistanceをreturn、mainで出力(問題1番) for(int i = 0; i < b_size; i++){ for(int j = 0; j < b_size; j++){ path[i][j] = visited[i][j]; } } return v.distance; } auto next_v = v; next_v.distance = v.distance + 1; //次に行くvertixのdistanceを1プラスする next_v.position = v.position + movements[i]; //次に行くvertix設定 if((next_v.position.x > b_size) || (next_v.position.y > b_size)){ //boardの範囲を超えたらcontinue continue; } else if((next_v.position.x < 0) || (next_v.position.y < 0)){ //boardの範囲を超えたらcontinue continue; } if(visited[next_v.position.x][next_v.position.y] != 0){ //既に通ったvertexは何もしない continue; } visited[next_v.position.x][next_v.position.y] = next_v.distance + 1; //何回目でvertixに行けたか8x8の盤に保存 q.enqueue(next_v); //新しいvertixをenqueue } } return 0; }

イメージ説明

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

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

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

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

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

guest

回答4

0

int visited[b_size][b_size] = {0, };  //すべてのvertixを未訪問に設定

この情報だけでは目的を達成できない,と言うのであれば,「目的に必要な記録」を追加で取ればよいのでは.
例えば,「探索の過程で各マスにどのマスから到達したのか」とかいう記録も取るならば,ゴールからスタートまで辿れますよね.

投稿2022/06/20 03:59

fana

総合スコア11658

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

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

fana

2022/06/20 04:02

「ルートを保存」したいというのであれば,直接的にルート情報を作ればいいよね,っていう.
guest

0

一応、今の構造のままでも、最短経路を返すことは可能です。(BFSで見つけた経路と同じとは限りませんが)
destからfirまで、visitedが減る順番でナイトを動かせば、それが最短経路となります。
あとは、たどったところだけvisitedからpathに転記すればよいでしょう。

こちらのリンク先に、単純な迷路における最短経路復元方法の説明があります。移動をナイトの動きに変えてあげれば、この問題の解決にも応用できます。なお、私の方法が「方法1」、他の方が説明しているのが「方法2」に対応します。
意外と解説がない!動的計画法で得た最適解を「復元」する一般的な方法

投稿2022/06/19 05:45

編集2022/07/12 14:33
actorbug

総合スコア2224

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

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

0

せっかくなので、
queueにパスをためていく
→mainにqueueをまんまわたす
→パスを追うときにqueueからひとつずつ出していく
とかどうでしょうか?

Nodeとか使わずに済みますし!

c++

1#include <iostream> 2#include <queue> 3using namespace std; 4 5#define _SIZE 8 6 7int absolute(int a) { 8 return (a < 0) ? -1 * a : a; 9} 10 11struct Move 12{ 13 int x; 14 int y; 15 16 Move operator +(Move a) { 17 Move b; 18 b.x = this->x + a.x; 19 b.y = this->y + a.y; 20 return b; 21 } 22 Move operator -(Move a) { 23 Move b; 24 b.x = this->x - a.x; 25 b.y = this->y - a.y; 26 return b; 27 } 28 int operator <(Move a) { 29 return ((absolute(this->x) + absolute(this->y)) < (absolute(a.x) + absolute(a.y))); 30 } 31 int operator !=(Move a) { 32 return ((this->x != a.x) || (this->y != a.y)); 33 } 34}; 35 36int shortdistance(Move fir, Move dest, queue<Move>& q, Move* move) { 37 Move position = fir; 38 39 q.push(position); 40 41 while (position != dest) { 42 Move tem = position + move[0]; 43 for (int i = 1; i < 8; i++) { 44 if ((dest - (position + move[i])) < (dest - tem)) { 45 tem = position + move[i]; 46 } 47 } 48 position = tem; 49 q.push(position); 50 } 51 52 return q.size(); 53} 54 55int main(void) { 56 Move fir = { 0, 0 }; 57 Move dest = { 6, 7 }; 58 Move* move = new Move[8]; 59 queue<Move> path; 60 61 move[0] = { 2, 1 }; 62 move[1] = { 2, -1 }; 63 move[2] = { 1, 2 }; 64 move[3] = { 1, -2 }; 65 move[4] = { -2, 1 }; 66 move[5] = { -2, -1 }; 67 move[6] = { -1, 2 }; 68 move[7] = { -1, -2 }; 69 70 int count = shortdistance(fir, dest, path, move); 71 72 cout << "到着時の移動回数 : " << count << '\n' << endl; 73 74 for (int i = 1; i <= count; i++) { 75 Move tem = path.front(); 76 cout << i << "回目 : [" << tem.x << ", " << tem.y << "]" << endl; 77 path.pop(); 78 } 79 80 return 0; 81}

投稿2022/06/19 05:01

X_III

総合スコア3

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

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

can110

2022/06/19 05:29

ためしに「Move fir = { 1, 1 }」で動かしてみたが、終わらないようです。 到達済みを記録する必要はないでしょうか。
actorbug

2022/06/19 20:16 編集

Move dest = { 1, 1 }; にして実行すると、どうなるでしょうか。 ちなみに、到達済みを記録する修正をしたとしても、このコードだと最短経路は見つかりません。 (0,0)→(1,2)→(3,1)→(2,3)→(1,1)
guest

0

単純なのは現在位置を表す変数positionを、これまで到達した位置の配列positionsに変更することでしょうか。
条件を満たしたNodeをキューに追加する前に、現在位置を配列に追加します。
ゴールに到達したら、それらから順番と位置が分かります。
なお、この変更によりdistancepositionsの要素数から算出できるので不要になります。

投稿2022/06/18 23:43

can110

総合スコア38266

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問