Eight poj1077 广度优先搜索

http://poj.org/problem?id=1077

这个题在poj用朴素的算法就可以了。

在hdu上需要用双向广搜才可以。

首先贴一个单向广搜。

#include <iostream>
#include <bitset>
using namespace std;

int nGoalStatus;//目标状态

/*声明了一个含有9!个位的bitset对象,位的顺序从0到31,缺省情况下所有的位都被初始化为0 */
bitset<362880> Flags; //节点是否标记

char szResult[400000];//结果
char szMoves[400000];//移动步骤 u/d/r/l
int anFather[400000];//父节点指针
int MyQueue[400000];//状态队列,状态总数362880
int nQHead,nQTail;
char sz4Moves[]="udrl";//四种动作

template<class T>
unsigned int GetPermutationNum(T *first,T *permutation,int len){
//permutation编号从0开始算,[first,first+len)里面存放着第0号permutation,排列的每个元素都不一样,返回排列的编号
	unsigned int factorial[21]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,1932053504,1278945280,2004310016,2004189184,4006445056,3396534272,109641728,2192834560};
	bool used[21] = {0};
	int perInt[21]; //要转换成[0,len-1]的整数的排列
	for(int i=0;i<len;++i)
		for( int j=0; j<len;++j){
			if(*(permutation+i) == *(first+j)){
				perInt[i]=j;
				break;
			}
		}
		unsigned int num=0;
		for( int i=0;i<len;++i){
			unsigned int n=0;
			for( int j=0;j<perInt[i];++j){
				if(!used[j]) 
					++n;
			}
			num+=n*factorial[len-i-1];
			used[perInt[i]]=true;
		}
		return num;
}

template <class T>
void GenPermutationByNum(T *first,T *permutation,int len,unsigned int No){ //根据排列编号,生成排列
//[first,first+len) 里面放着第0号 permutation,,排列的每个元素都不一样
	unsigned int factorial[21] ={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,1932053504,1278945280,2004310016,2004189184,4006445056,3396534272,109641728,2192834560};
	bool used[21]={0};
	int perInt[21]; //要转换成 [0,len-1] 的整数的排列
	for(int i=0;i<len;++i){
		unsigned int tmp;
		int n=0;
		int j;
		for(j=0;j<len;++j){
			if(!used[j]){
				if(factorial[len-i-1]>= No+1)
					break;
				else No-=factorial[len-i-1];
			}
		}
		perInt[i]=j;
		used[j]=true;
	}
	for(int i=0;i<len;++i) 
		*(permutation+i)=*(first+perInt[i]);
}

int StrStatusTointStatus(const char *strStatus){
	return GetPermutationNum("012345678",strStatus,9);
}

void IntStatusToStrStatus(int n,char *StrStatus){
	GenPermutationByNum((char *)"012345678",StrStatus,9,n);
}

int NewStatus(int nStatus,char cMove){
//求从nStatus经过cMove移动后得到的新状态。若移动不可行则返回-1
	char szTmp[20];
	int nZeroPos;
	IntStatusToStrStatus(nStatus,szTmp);
	int i;
	for (i=0;i<9;i++){
		if (szTmp[i]=='0'){
			nZeroPos=i;
			break;
		}
	}//返回空格的位置
	switch(cMove){
	case 'u':
		if (nZeroPos-3<0)
			return -1;
		//空格在第一行
		else{
			szTmp[nZeroPos]=szTmp[nZeroPos-3];
			szTmp[nZeroPos-3]='0';
		}
		break;
	case 'd':
		if (nZeroPos+3>8)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos+3];
			szTmp[nZeroPos+3]='0';
		}
		break;
	case 'l':
		if(nZeroPos%3==0)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos-1];
			szTmp[nZeroPos-1]='0';
		}
		break;
	case 'r':
		if (nZeroPos%3==2)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos+1];
			szTmp[nZeroPos+1]='0';
		}
	}
	return StrStatusTointStatus(szTmp);
}

bool Bfs(int nStatus){
	int nNewStatus,i;
	Flags.reset();//将所有位置0
	nQHead=0;
	nQTail=1;
	MyQueue[nQHead]=nStatus;
	while (nQHead!=nQTail){//队列不为空
		nStatus=MyQueue[nQHead];
		if (nStatus==nGoalStatus)
			return true;
		for (i=0;i<4;i++){
			nNewStatus=NewStatus(nStatus,sz4Moves[i]);
			if(nNewStatus==-1)
				continue;
			if (Flags[nNewStatus])
				continue;
			Flags.set(nNewStatus,true);
			MyQueue[nQTail]=nNewStatus;
			anFather[nQTail]=nQHead;//记录父节点
			//记录本节点是由父节点经什么动作而来
			szMoves[nQTail]=sz4Moves[i];
			nQTail++;
		}
		nQHead++;
	}
	return false;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	nGoalStatus=StrStatusTointStatus("123456780");
	char szLine[50];
	char szLine2[20];
	while (cin.getline(szLine,48)){
		int i,j;
		//将输入的原始字符串变为九进制字符串
		for (i=0,j=0;szLine[i];i++)
			if(szLine[i]!=' '){
				if(szLine[i]=='x')
					szLine2[j++]='0';
				else szLine2[j++]=szLine[i];
			}
		szLine2[j]='\0';

		/*接下来的步骤用来判断该串是否有解 偶序列和奇序列不能相互转换。只能目标序列和源序列同为奇序列或者偶序列才可能有解决方案。*/
		int sumGoal=0;
		for (i=0;i<8;i++)
			sumGoal+=i-1;
		int sumOri=0;
		for (i=0;i<9;i++){
			if(szLine2[i]=='0')
				continue;
			for(j=0;j<i;j++)
				if(szLine2[j]<szLine2[i]&&szLine2[j]!='0')
					sumOri++;
		}
		if (sumOri%2!=sumGoal%2){
			cout<<"unsolvable"<<endl;
			continue;
		}

		if(Bfs(StrStatusTointStatus(szLine2))){
			int nMoves=0;
			int nPos=nQHead;
			do{ //通过anFather数组找到成功的状态序列,输出相应步骤
				szResult[nMoves++]=szMoves[nPos];
				nPos=anFather[nPos];
			}while (nPos);
			for(i=nMoves-1;i>=0;i--)
				cout<<szResult[i];
			cout<<endl;
		}
		else 
			cout<<"unsolvable"<<endl;
	}
}

然后再 粘一个双向广搜。

下面的代码可以过hdu1043,上面的代码过不去...

#include <iostream>
#include <bitset>
using namespace std;

int nGoalStatus;//目标状态

/*声明了一个含有9!个位的bitset对象,位的顺序从0到31,缺省情况下所有的位都被初始化为0 */
bitset<362880> Flags[2]; //节点是否标记

char szResult[400000];//结果
char szMoves[2][400000];//移动步骤 u/d/r/l
int anFather[2][400000];//父节点指针
int Queue[2][400000];//状态队列,状态总数362880
int QHead[2],QTail[2];
char sz4Moves[]="udrl";//四种动作

int matchingStatus;//双向碰到的那个状态
int matchingQ;//队列matchingQ的对头元素是双向碰到的那个状态

template<class T>
unsigned int GetPermutationNum(T *first,T *permutation,int len){
	//permutation编号从0开始算,[first,first+len)里面存放着第0号permutation,排列的每个元素都不一样,返回排列的编号
	unsigned int factorial[21]={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,1932053504,1278945280,2004310016,2004189184,4006445056,3396534272,109641728,2192834560};
	bool used[21] = {0};
	int perInt[21]; //要转换成[0,len-1]的整数的排列
	for(int i=0;i<len;++i)
		for( int j=0; j<len;++j){
			if(*(permutation+i) == *(first+j)){
				perInt[i]=j;
				break;
			}
		}
		unsigned int num=0;
		for( int i=0;i<len;++i){
			unsigned int n=0;
			for( int j=0;j<perInt[i];++j){
				if(!used[j]) 
					++n;
			}
			num+=n*factorial[len-i-1];
			used[perInt[i]]=true;
		}
		return num;
}

template <class T>
void GenPermutationByNum(T *first,T *permutation,int len,unsigned int No){ //根据排列编号,生成排列
	//[first,first+len) 里面放着第0号 permutation,,排列的每个元素都不一样
	unsigned int factorial[21] ={1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,1932053504,1278945280,2004310016,2004189184,4006445056,3396534272,109641728,2192834560};
	bool used[21]={0};
	int perInt[21]; //要转换成 [0,len-1] 的整数的排列
	for(int i=0;i<len;++i){
		unsigned int tmp;
		int n=0;
		int j;
		for(j=0;j<len;++j){
			if(!used[j]){
				if(factorial[len-i-1]>= No+1)
					break;
				else No-=factorial[len-i-1];
			}
		}
		perInt[i]=j;
		used[j]=true;
	}
	for(int i=0;i<len;++i) 
		*(permutation+i)=*(first+perInt[i]);
}

int StrStatusTointStatus(const char *strStatus){
	return GetPermutationNum("012345678",strStatus,9);
}

void IntStatusToStrStatus(int n,char *StrStatus){
	GenPermutationByNum((char *)"012345678",StrStatus,9,n);
}

int NewStatus(int nStatus,char cMove){
	//求从nStatus经过cMove移动后得到的新状态。若移动不可行则返回-1
	char szTmp[20];
	int nZeroPos;
	IntStatusToStrStatus(nStatus,szTmp);
	int i;
	for (i=0;i<9;i++){
		if (szTmp[i]=='0'){
			nZeroPos=i;
			break;
		}
	}//返回空格的位置
	switch(cMove){
	case 'u':
		if (nZeroPos-3<0)
			return -1;
		//空格在第一行
		else{
			szTmp[nZeroPos]=szTmp[nZeroPos-3];
			szTmp[nZeroPos-3]='0';
		}
		break;
	case 'd':
		if (nZeroPos+3>8)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos+3];
			szTmp[nZeroPos+3]='0';
		}
		break;
	case 'l':
		if(nZeroPos%3==0)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos-1];
			szTmp[nZeroPos-1]='0';
		}
		break;
	case 'r':
		if (nZeroPos%3==2)
			return -1;
		else {
			szTmp[nZeroPos]=szTmp[nZeroPos+1];
			szTmp[nZeroPos+1]='0';
		}
	}
	return StrStatusTointStatus(szTmp);
}

char ReverseMove(char c){
	switch(c){
	case 'u':
		return 'd';
	case 'l':
		return 'r';
	case 'r':
		return 'l';
	case  'd':
		return 'u';
	}
	return 0;
}

bool DBfs(int nStatus){
	int nNewStatus;
	int i;
	for (i=0;i<2;i++){
		Flags[i].reset();
		QHead[i]=0;
		QTail[i]=1;
	}
	Queue[0][0]=nStatus;
	Queue[1][0]=nGoalStatus;
	Flags[0][nStatus]=1;
	Flags[1][nGoalStatus]=1;
	
	while (QHead[0]!=QTail[0]&&QHead[1]!=QTail[1]){
		int qNo;
		if(QHead[0]==QTail[0])
			qNo=1;
		else if(QHead[1]==QTail[1])
			qNo=0;
		else{
			if (QHead[0]-QTail[0]>QHead[1]-QTail[1])
				qNo=0;
			else qNo=1; 
		}
		int vqNo=1-qNo;
		nStatus=Queue[qNo][QHead[qNo]];
		if (Flags[vqNo][nStatus]==1){//取出的元素曾在另一队列出现过
			matchingStatus=nStatus;
			matchingQ=qNo;
			return true;
		}
		else {
			for (i=0;i<4;i++){
				nNewStatus=NewStatus(nStatus,sz4Moves[i]);
				if (nNewStatus==-1)
					continue;
				if (Flags[qNo][nNewStatus])
					continue;
				Flags[qNo][nNewStatus]=1;
				Queue[qNo][QTail[qNo]]=nNewStatus;
				anFather[qNo][QTail[qNo]]=QHead[qNo];
				szMoves[qNo][QTail[qNo]]=sz4Moves[i];
				QTail[qNo]++;
			}
			QHead[qNo]++;
		}
	}
	return false;
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	nGoalStatus=StrStatusTointStatus("123456780");
	char szLine[50];
	char szLine2[20];
	while (cin.getline(szLine,48)){
		int i,j;
		//将输入的原始字符串变为九进制字符串
		for (i=0,j=0;szLine[i];i++)
			if(szLine[i]!=' '){
				if(szLine[i]=='x')
					szLine2[j++]='0';
				else szLine2[j++]=szLine[i];
			}
			szLine2[j]='\0';

			/*接下来的步骤用来判断该串是否有解 偶序列和奇序列不能相互转换。只能目标序列和源序列同为奇序列或者偶序列才可能有解决方案。*/  
			int sumGoal=0;
			for (i=0;i<8;i++)
				sumGoal+=i-1;
			int sumOri=0;
			for (i=0;i<9;i++){
				if(szLine2[i]=='0')
					continue;
				for(j=0;j<i;j++)
					if(szLine2[j]<szLine2[i]&&szLine2[j]!='0')
						sumOri++;
			}
			if (sumOri%2!=sumGoal%2){
				cout<<"unsolvable"<<endl;
				continue;
			}

			if(DBfs(StrStatusTointStatus(szLine2))){
				int nMoves=0;
				int Pos,i;
				if (matchingQ==0)
					Pos=QHead[0];
				else {
					for (i=0;i<QTail[0];i++)
						if (Queue[0][i]==matchingStatus){
							Pos=i;
							break;
						}
				}
				do {
					if (Pos){
						szResult[nMoves++]=szMoves[0][Pos];
						Pos=anFather[0][Pos];
					}
				}while (Pos);
				reverse(szResult,szResult+nMoves);

				if (matchingQ==0){
					for (i=0;i<QTail[1];i++)
						if (Queue[1][i]==matchingStatus){
							Pos=i;
							break;
						}
				}
				else Pos=QHead[1];

				do {
					if (Pos){
						szResult[nMoves++]=ReverseMove(szMoves[1][Pos]);
						Pos=anFather[1][Pos];
					}
				}while(Pos);
				for (i=0;i<nMoves;i++)
					cout<<szResult[i];
				cout<<endl;
			}
			else 
				cout<<"unsolvable"<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值