生成迷宫的小程序,前两天写的,留个存档。

fireseed 2009-02-17 01:04:12
算法简介
把N行M列的迷宫认为是N行M-1列的水平墙和N-1行M列的竖直墙组成的图
N*M的迷宫中一共有(N-1)*(M-1)个通路节点,节点和墙分开存储
每个节点都有上下左右四个方向上的邻点
从入口节点开始执行深度递归遍例,遇到外墙或出口节点则返回
以随机的方式选择邻点,然后再以邻点进行遍例,直到所有节点都被遍例
遍例过程中,每通过一个点就将其标记为已过
向四个方向中的某个方向移动时就把在那个方向上穿过的墙标记为已过
递归过程完成后就得到水平墙和竖直墙的二维数组,1为可过,0为有墙
输出过程不具普适性,不做详解。

注:
1. 本算法复杂度为o(n^2)。
2. 本算法只能适合生成边长较小的迷宫(一般不超过50,依程序栈的大小而定)。如果想要生成大的,必须将递归改为循环,自行定义栈的数据结构。在递归调用函数时将状态变量压栈,然后用goto语句跳转到函数入口,返回时将栈顶的状态变量弹出,再用goto语句跳回。编程思路简单,但实现较繁索,这里就不再赘述了。


#include <stdio.h>
#include <stdlib.h>

struct SPOINT
{
int nX;
int nY;
};

struct SMAZEINFO
{
char *pWallHor;
char *pWallVer;
int nWidth;
int nHeight;
char *pPoints;
};

void _GenMaze( SMAZEINFO *pMazeInfo, SPOINT *pCurPos )
{ // 此函数为递归函数,执行图的深度遍例
const float c_f2PI = 3.1415927f / 2.0f;
const int c_nNeiCnt = 4;
SPOINT aNeighbors[c_nNeiCnt] = { // 邻点数组,初始值为偏移量
{ 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
int nPtCntX = pMazeInfo->nWidth - 1;
int nPtCntY = pMazeInfo->nHeight - 1;
int nX = pCurPos->nX;
int nY = pCurPos->nY;
// 标记当前点已遍例
pMazeInfo->pPoints[ nY * nPtCntX + nX ] = true;
if ( nX != nPtCntX - 1 || nY != nPtCntY - 1 )
{ // 最后一个点不遍例邻点
for ( int i = 0; i < c_nNeiCnt; ++ i )
{ // 循环生成邻点
aNeighbors[i].nX += pCurPos->nX;
aNeighbors[i].nY += pCurPos->nY;
}
for ( int i = 0; i < c_nNeiCnt; ++ i )
{ // 乱序邻点,标准shuffle算法
int nRandId = rand() % c_nNeiCnt;
if ( nRandId != i )
{
SPOINT ptTemp = aNeighbors[i];
aNeighbors[i] = aNeighbors[nRandId];
aNeighbors[nRandId] = ptTemp;
}
}
for ( int i = 0; i < 4; i++ )
{ // 遍例相邻点
nX = aNeighbors[i].nX;
nY = aNeighbors[i].nY;
if ( nX >= 0 && nY >= 0 && nX < nPtCntX && nY < nPtCntY &&
!pMazeInfo->pPoints[ nY * nPtCntX + nX ] )
{ //去除超过边界的点和已遍例的点
if ( nX != pCurPos->nX )
{ // 横向通过,打通竖直墙
int nOffset = nX > pCurPos->nX;
pMazeInfo->pWallVer[ pCurPos->nY * pMazeInfo->nWidth +
pCurPos->nX + nOffset ] = 1;
}
else
{ // 纵向通过,打通水平墙
int nOffset = nY > pCurPos->nY;
pMazeInfo->pWallHor[ ( pCurPos->nY + nOffset ) *
nPtCntX + pCurPos->nX ] = 1;
}
_GenMaze( pMazeInfo, &aNeighbors[i] );
}
}
}
}

void GenMaze( char *pWallHor, char *pWallVer, int nWidth, int nHeight )
{
int nPtCntX = nWidth - 1;
int nPtCntY = nHeight - 1;
char *pPoints = new char[ nPtCntX * nPtCntY ];
memset( pPoints, 0, nPtCntX * nPtCntY );
memset( pWallHor, 0, ( nWidth - 1 ) * nHeight );
memset( pWallVer, 0, nWidth * ( nHeight - 1 ) );
pWallHor[0] = 1; // 打通入口
SMAZEINFO MazeInfo;
MazeInfo.pWallVer = pWallVer;
MazeInfo.pWallHor = pWallHor;
MazeInfo.pPoints = pPoints;
MazeInfo.nWidth = nWidth;
MazeInfo.nHeight = nHeight;
SPOINT ptFirst = { 0 };
_GenMaze( &MazeInfo, &ptFirst );
pWallHor[ ( nWidth - 1 ) * nHeight - 1 ] = 1; // 打通出口
}


void main()
{
srand( (unsigned int)time(0) );
const int nWidth = 15; // 在这里输入迷宫的宽度
const int nHeight = 15; // 在这里输入迷宫的高度
char aWallHor[ ( nWidth - 1 ) * nHeight ];
char aWallVer[ nWidth * ( nHeight - 1 ) ];
GenMaze( aWallHor, aWallVer, nWidth, nHeight );
for ( int i = 0; i < nHeight - 1; ++i )
{
printf( "@@" );
for ( int j = 0; j < nWidth - 1; ++j )
{
printf( aWallHor[ i * ( nWidth - 1 ) + j ] ? " @@" : "@@@@" );
}
printf( "\n@@" );
for ( int j = 1; j < nWidth; ++j )
{
printf( aWallVer[ i * nWidth + j ] ? " " : " @@" );
}
printf( "\n" );
}
printf( "@@" );
for ( int j = 0; j < nWidth - 1; ++j )
{
printf( aWallHor[ ( nHeight - 1 ) * ( nWidth - 1 ) + j ] ?
" @@" : "@@@@" );
}
getch();
return;
}
...全文
386 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
jikeyuan1 2011-04-07
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 sanguomi 的回复:]

一看楼主的头像就知道楼主是智慧型的,呵呵
[/Quote]
同感!!!
luciferdragon 2010-10-10
  • 打赏
  • 举报
回复
收获不少感谢楼主!
nk_ysg 2009-03-14
  • 打赏
  • 举报
回复
进来学习下
ztenv 版主 2009-03-14
  • 打赏
  • 举报
回复
在下提一句:抽象的好像并不好。
Rain208 2009-03-14
  • 打赏
  • 举报
回复
Pajack 2009-03-14
  • 打赏
  • 举报
回复
lz好强,期待更多好算法。。
n_yHHy_n 2009-02-22
  • 打赏
  • 举报
回复
mark
lei_zhuyan 2009-02-22
  • 打赏
  • 举报
回复
有空看看
fireseed 2009-02-22
  • 打赏
  • 举报
回复

// 以下算法计算n的m次方
// m的定义域是[1,2^31],n的定义域是[0,65535]
// 原理就是按位相乘,处理进位
#include <iostream>
#include <vector>
#include <stdlib.h>
void main( void )
{
int n = 26, m = 20;
div_t dr = { 0 };
std::vector<int> vecNum( 1, n ); // 用vector存储大数,首位赋n
for ( int i = 1; i < m; ++i, dr.quot = 0 ) // 每次将上次的商清零
{ // 循环乘m次
for ( std::vector<int>::iterator cur = vecNum.begin();
cur != vecNum.end(); *(cur++) = dr.rem )
{ // 对大数的每一位都乘以n
*cur = *cur * n + dr.quot; // 加上上一位的余数
dr = div( *cur, 10 ); // 取10的余,保留个位数
} // 下面的循环处理进位使位数增加的情况
for ( dr = div( dr.quot, 10 ); dr.quot || dr.rem;
dr = div( dr.quot, 10 ) ) vecNum.push_back( dr.rem );
} // 下面输出,从最后的最高位开始
for ( std::vector<int>::reverse_iterator rcur = vecNum.rbegin();
rcur != vecNum.rend(); ++rcur ) std::cout << *rcur;
}

a574099340123 2009-02-20
  • 打赏
  • 举报
回复
看不懂呀!
夹心饼干 2009-02-20
  • 打赏
  • 举报
回复
学习下
sanguomi 2009-02-20
  • 打赏
  • 举报
回复
一看楼主的头像就知道楼主是智慧型的,呵呵
koo8 2009-02-20
  • 打赏
  • 举报
回复
顶起来,学习!
pengzhixi 2009-02-20
  • 打赏
  • 举报
回复
mark
fireseed 2009-02-20
  • 打赏
  • 举报
回复
全组合算法:


// 算法说明:当n大于2时,n个数的全组合一共有(2^n)-1种。
// 当对n个元素进行全组合的时候,可以用一个n位的二进制数表示取法。
// 1表示在该位取,0表示不取。例如,对ABC三个元素进行全组合,
// 100表示取A,010表示取B,001表示取C,101表示取AC
// 110表示取AB,011表示取BC,111表示取ABC
// 注意到表示取法的二进制数其实就是从1到7的十进制数
// 推广到对n个元素进行全排列,取法就是从1到2^n-1的所有二进制形式
// 要取得2^n,只需将0xFFFFFFFF左移32-n位,再右移回来就可以了。
void main( void )
{
string str[] = { "A", "B", "C", "D", "E" };
unsigned int nCnt = sizeof( str ) / sizeof( str[0] );
int nBit = ( ( 0xFFFFFFFFU << ( 32 - nCnt ) ) >> ( 32 - nCnt ) );
for ( int i = 1; i <= nBit; ++i ) {
for ( unsigned int j = 0; j < nCnt; ++j )
if ( ( i << ( 31 - j ) ) >> 31 ) std::cout << str[j];
std::cout << '\n';
}
}


全排列算法:



// 以下算法完成n个元素的全排列
// 思路来自Knuth的TAOCP第4卷,确实太复杂,我也不是很明白。
// 大概是用q来表示将要换位的元素。
// q和q+-1进行交换,每次交换就得到一个新的排列。
// 利用一个n个元素的辅助数组,实现对q的递增和递减,
// 用该数组的进位实现q的突变。
// 复杂度是线性的,实现代码也进行了充分的优化,效率奇高。
#include <iostream>
#include <algorithm>
void main( void )
{
string aNum[] = { "A", "B", "C", "D", "E" };
const int nCnt = sizeof( aNum ) / sizeof( aNum[0] );
int aC[nCnt] = { 0 }, aD[nCnt];
int nFac = 1; // 计算nCnt的阶乘,顺便将aD全赋为1
for ( int i = 1; i <= nCnt; nFac *= i, ++i ) aD[ i - 1 ] = 1;
for ( int i = 0, s = 0, q = 0; q != i + 1 || i != 0; aC[i] = q, s = 0 )
{ // 输出一组全排列
for ( int j = 0; j < nCnt; ++j ) std::cout << aNum[j];
std::cout << endl;
for ( i = nCnt - 1, q = aC[i] + aD[i]; // 以下的算法就无法说清了
q < 0 || ( q == i + 1 && i != 0 );--i, q = aC[i] + aD[i] ) {
s += ( q == i + 1 );
aD[i] = -aD[i];
} // 下面是交换两个相临的变量
std::swap( aNum[ i - aC[i] + s ], aNum[ i - q + s ] );
}
}


高精度计时器类


class CTimer
{
public:
inline CTimer( void )
{
QueryPerformanceFrequency( (LARGE_INTEGER*)&m_nFreq );
QueryPerformanceCounter( (LARGE_INTEGER*)&m_nStart );
}
inline double Reset( void )
{
__int64 nLast = m_nStart;
QueryPerformanceCounter( (LARGE_INTEGER*)&m_nStart );
return double( m_nStart - nLast ) / (double)m_nFreq;
}
inline double Now( void ) const
{
__int64 nCurCnt;
QueryPerformanceCounter( (LARGE_INTEGER*)&nCurCnt );
return double( nCurCnt - m_nStart ) / (double)m_nFreq;
}
private:
__int64 m_nFreq;
__int64 m_nStart;
};


贴不了了,来人回复一下,还有好多
fireseed 2009-02-17
  • 打赏
  • 举报
回复
这是前两天在C#版一个贴子中给出的答案,但该贴主不懂C++,真是浪废感情,现在发在这里,希望能得到批评!

64,646

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧