利用不相交集画迷宫

当我们画一个80*50的迷宫时相当于是画了个80*50的方格子,把格子和格子之间的某些墙拆掉,互相连通的格子我们称之为属于同一个集合,如果两个集合不连通,那就称之为不相交集.

画迷宫的过程就是不断地拆除格子与格子之间的墙,直到第一个格子和最后一个格子属于同一个集合算法终止.

不相交集类.

#ifndef _DISJSETS_H
#define _DISJSETS_H

#include <vector>
using namespace std;

class DisjSets{
public:
	explicit DisjSets(int numElems);

	int find(int x);
	void unionSets(int root1,int root2);
	
private:
	vector<int> s;
};

#endif

类的实现

#include "DisjSets.h"

DisjSets::DisjSets(int numElems):s(numElems){
	for(int i=0;i<s.size();i++)
		s[i]=-1;
}

//按大小求并,s[i]表示i节点的父节点,如果i已是根节点,则s[i]存储树的大小的相反数
void DisjSets::unionSets(int root1,int root2){
	if(s[root2]<s[root1]){		//root2 is bigger
		s[root2]+=s[root1];		//size of root2 increase
		s[root1]=root2;			//make root2 a new root
	}
	else{
		s[root1]+=s[root2];
		s[root2]=root1;
	}
}

//边find边路径压缩
int DisjSets::find(int x){
	if(s[x]<0)
		return x;
	else
		//从X到根的路径上的每一个节点都使它的父节点变成根节点
		return s[x]=find(s[x]);
}

画迷宫

#include "DisjSets.h"
#include <ctime>
#include <cassert>
#include <cstdlib>
#include <iostream>

int getAdj(int x,int WIDTH,int SIZE);

int main(int argc,char *argv[]){
	int WIDTH=5;
	int HEIGHT=5;
	
	if(argc==3){
		WIDTH=atoi(argv[1]);
		HEIGHT=atoi(argv[2]);
	}
	int SIZE=WIDTH*HEIGHT;
	srand((unsigned)time(NULL));
	DisjSets labyrinth(SIZE);
	vector<pair<int,int> > grap(SIZE);
	
	for(;;){
		if(labyrinth.find(0)==labyrinth.find(SIZE-1))
			break;
		int m=rand()%SIZE;
		int n=getAdj(m,WIDTH,SIZE);
		int root1=labyrinth.find(m);
		int root2=labyrinth.find(n);
		if(root1!=root2){
			labyrinth.unionSets(root1,root2);
			if(n==m-WIDTH)
				grap[n].second=1;		//n下面的那堵墙拆掉
			else if(n==m+1)
				grap[m].first=1;		//m右面的那堵墙拆掉
			else if(n==m+WIDTH)
				grap[m].second=1;		//m下面的那堵墙拆掉
			else if(n==m-1)
				grap[n].first=1;		//n右面的那堵墙拆掉
		}
	}
	grap[SIZE-1].first=grap[SIZE-1].second=1;		//最后那一格子的右面和下面的墙都要拆掉
	
	for(int i=0;i<HEIGHT;i++){
		for(int j=0;j<WIDTH;j++){
			if(grap[i*WIDTH+j].first==1 && grap[i*WIDTH+j].second==1)
				cout<<"O";
			else if(grap[i*WIDTH+j].first==0 && grap[i*WIDTH+j].second==0)
				cout<<"C";
			else if(grap[i*WIDTH+j].first==1 && grap[i*WIDTH+j].second==0)
				cout<<"R";
			else if(grap[i*WIDTH+j].first==0 && grap[i*WIDTH+j].second==1)
				cout<<"D";
		}
		cout<<endl;
	}
	return 0;
}

//迷宫的每个格子中用从0开始的自然数填充.下面函数用于找到x格子的一个随机的相邻格子
int getAdj(int x,int WIDTH,int SIZE){
	assert( x>=0 && x<SIZE);
	int ret=-1;
	
	while(ret<0 || ret>=SIZE){				//首先保证x格子的邻居没有走出迷宫的范围
		int r=rand();
		
		if(x%WIDTH==0){						//x格子在迷宫的最左边
			if(r%3==0)
				ret=x+1;
			else if(r%3==1)
				ret=x+WIDTH;
			else if(r%3==2)
				ret=x-WIDTH;
		}
		else if((x+1)%WIDTH==0){			//x格子在迷宫的最右边
			if(r%3==0)
				ret=x-1;
			else if(r%3==1)
				ret=x+WIDTH;
			else if(r%3==2)
				ret=x-WIDTH;
		}
		else{								//其他
			if(r%4==0)
				ret=x-WIDTH;
			else if(r%4==1)
				ret=x+1;
			else if(r%4==2)
				ret=x+WIDTH;
			else if(r%4==3)
				ret=x-1;
		}
	}
	return ret;
}

运行

2011033009404242.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值