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;
}