内容は、壁とスペースで構成された迷路が与えられたとき、スタート地点からゴール地点に至る最短経路を求めよ、というものです。とのこと。ふむ、なかなか面白そうじゃない! 独りでやるのも寂しいので、学校の先生と競争してみることに。
たとえば、S:スタート G:ゴール *:壁 $:解答の経路 としたとき、
************************** *S* * * * * * * ************* * * * * ************ * * * * ************** *********** * * ** *********************** * * G * * * *********** * * * * ******* * * * * * **************************
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
typedef unsigned char byte;
typedef unsigned int uint;
typedef std::vector<std::string> StringList;
using namespace std;
/**
* 2次元配列の位置などを表す構造体
*/
struct POS{
uint x, y;
};
//定数
const char *fname = "dat.txt";//ファイルデータパス
const byte WALL = -1;//壁を表す定数
//
// 再帰的にスタートからゴールへ向かって最短距離を求める
// @param now 現在位置
// @param from 一つ前の位置
// @param dat 書き換えるデータ配列
// @param width データ配列幅
// @param start スタート地点
// @param goal ゴール地点
// @return none
//
void SearchPath(
uint count,
const POS &now,
const POS &from,
byte *dat,
uint width,
const POS &start,
const POS &goal
)
{
//再帰の終了をチェック
if(now.x == goal.x && now.y == goal.y){
dat[now.x+now.y*width] = count+1;
}
//次に移動する座標を決定する
POS next[4] ={
{now.x, now.y-1},//上
{now.x, now.y+1},//下
{now.x-1, now.y},//左
{now.x+1, now.y},//右
};
for(uint i=0; i<4; ++i){
uint idx = next[i].x + next[i].y*width;//次に調べる位置
//次の位置が壁なら調べない
if(dat[idx] == WALL){
continue;
}
//次に調べる位置が既に調べてあり
//かつ、すでに最短パスが出ていたらこれ以上調べない
if(dat[idx] > 0 && dat[idx] < count){
continue;
}
//現在の位置にこれまでの歩数を記録する
dat[now.x+now.y*width] = count;
//次の経路を再帰的に調べる
SearchPath(count+1, next[i], now, dat, width, start, goal);
}
}
//
// ゴール地点からスタート地点へ向かって最短経路を描画する
// @param sl 書き出す文字列リスト
// @param now 現在位置への参照
// @param start スタート地点
// @param goal ゴール地点
// @param dat ステップ数が記述されたデータ配列(壁は0xff)
// @param width データ配列幅
//
void FindPath(
StringList *sl,
const POS &now,
const POS &start,
const POS &goal,
const byte *dat,
uint width
)
{
//再帰打ち切りの条件
//すなわち、現在地がスタート地点なら終了
if(now.x == start.x && now.y == start.y){
return;
}
//次に移動する座標を決定する
POS next[4] ={
{now.x, now.y-1},//上
{now.x, now.y+1},//下
{now.x-1, now.y},//左
{now.x+1, now.y},//右
};
//現在のステップ数
uint nowStep = dat[now.x+now.y*width];
for(uint i=0; i<4; ++i){
uint idx = next[i].x + next[i].y*width;//次に調べる位置
//次の位置が壁なら調べない
if(dat[idx] == WALL){
continue;
}
//次の位置が現在のステップ数より小さいときは
//再帰的に調べる
if(dat[idx] == nowStep-1){
//経路マークをつける
char mark = '$';
if(now.x == goal.x && now.y == goal.y){
mark = 'G';
}
sl->at(now.y).at(now.x) = mark;
//次の経路を再帰的に調べる
FindPath(sl, next[i], start, goal, dat, width);
return;
}
}
}
//
//エントリポイント
//
int main(){
//
// テキストからデータを読み込んで配列に格納
//
std::fstream fs(fname);
StringList sl;
while(!fs.eof()){
string tmp;
getline(fs, tmp, '\n');
sl.push_back(tmp);
}
//数値に変換して配列に格納
uint height = sl.size();//配列高さ
uint width = sl[0].size();//配列幅
byte *dat = new byte [width*height];//作業用配列
POS start = {0,0};//スタート
POS goal = {0,0};//ゴール
POS now = {0,0};//現在地
for(uint y=0; y<height; ++y){
for(uint x=0; x<width; ++x){
uint idx = x+y*width;
dat[idx] = 0;
switch(sl[y][x]){
case '*':
dat[idx] = WALL;
break;
case 'G':
goal.x = x;
goal.y = y;
break;
case 'S':
start.x = now.x = x;
start.y = now.y = y;
break;
}
}
}
//
// 経路探索
// 上下左右に対して再帰的に検索をかける
//
SearchPath(0, start, start, dat, width, start, goal);
//探索したパスを元に最短経路を描画する
FindPath(&sl, goal, start, goal, dat, width);
//
//デバグ用表示
//
for(uint y=0; y<sl.size(); ++y){
cout << sl[y] << endl;
}
//
//ファイル書き出し
//
ofstream ofs("out.txt");
for(uint y=0; y<sl.size(); ++y){
ofs << sl[y] << endl;
}
ofs.close();
//あとしまつ
delete [] dat;
dat = 0;
return 0;
}
結果は惨敗。相手は1時間ちょっとで終わっていたのに対して、私は4時間かけてようやくといったところ。先生はPythonを使われていいたようです。てかこの手の問題でC++を使う時点でダメだろ俺…。Pythonなどの高級言語や、関数型言語ならもっともっと美しく簡潔に書けたでしょうね。とにかく自分の無能さが身に染みてわかった。くやしい!自分のアホさにげんなりした問題でした。さて、寝よう(:D)┼─┤
0 件のコメント:
コメントを投稿