蓝桥杯 九宫重排(八数码问题)

 历届试题 九宫重排   时间限制:1.0s   内存限制:256.0MB        
问题描述   如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

《蓝桥杯 九宫重排(八数码问题)》
《蓝桥杯 九宫重排(八数码问题)》

  我们把第一个图的局面记为:12345678.

  把第二个图的局面记为:123.46758

  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。

  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。 输入格式   输入第一行包含九宫的初态,第二行包含九宫的终态。 输出格式   输出最少的步数,如果不存在方案,则输出-1。 样例输入 12345678.

123.46758 样例输出 3 样例输入 13524678.

46758123. 样例输出 22

本题是A*算法的典型例题,练习之后可以对A*算法的原理有一定的理解。但是本文主要讲解另一种方法(A*算法抽空发布)。

本题主要的难点在于状态的表达,数学中有种方式叫康托展开,是一种可以解决一些序列问题的算法。例如可以把一个序列变为一个数字,排列3 5 7 4 1 2 9 6 8展开为98884,因为X=2*8!+3*7!+4*6!+2*5!+0*4!+0*3!+2*2!+0*1!+0*0!=98884。同理,也可以把数字转化为序列,叫做逆康托展开。(详情百度)

明白康托展开和逆康托展开后,就可以通过bfs求最少步数了。

代码:

#include <bits/stdc++.h> 
using namespace std;

const int maxn=500000;//康托展开的最大数字 
struct Node
{
    int val;
    int ans;
};
int fac[11]={1,1,2,6,24,120,720,5040,40320,362880};//0~8的阶乘 
int st,goal;
bool vis[maxn];//标记康托展开的数字 

int SetKT(char *str)//康托展开成数字 
{
    int ans=0;
    bool use[10];
    memset(use,0,sizeof(use));
    for(int i=0;i<9;i++)
    {
	    int val=str[i]-'0',cnt=0;
	    for(int j=1;j<val;j++)
	        if(!use[j])
		        cnt++;
	    ans+=cnt*fac[8-i];
	    use[val]=1;
    }
    return ans+1;
}

void GetKT(int val,char *str)//逆康拓展开,用数组str[]保存序列 
{
    val--;
    bool use[10];
    memset(use,0,sizeof(use));
    for(int i=0;i<9;i++)
    {
	    int num=val/fac[8-i],cnt=0;
	    val=val%fac[8-i];//余数 
	    for(int j=1;j<=9;j++)
	        if(!use[j])
		        if(++cnt==num+1)
		        {
		            str[i]=j+'0';
		            use[j]=1;
		            break;
		        }
    }
    str[9]='\0';
}

void Deal(char *str)
{
    for(int i=0;i<strlen(str);i++)
	if(str[i]=='.')
	    str[i]='9';
}

int bfs()
{
    queue<Node> q;
    memset(vis,0,sizeof(vis));
    Node node;
    node.val=st;
    node.ans=0;
    q.push(node);//进队列 
    vis[st]=1;//标记访问 
    while(!q.empty())
    {
	    Node p=q.front();
	    q.pop();
	    
	    char str[11];
	    GetKT(p.val,str);//解码 
	    
	    int index;
	    for(int i=0;i<strlen(str);i++)
	        if(str[i]=='9')//寻找空格位置 
	        {
		        index=i;
		        break;
	        }
	        
	    //根据空格位置进行操作 
	    if(index%3>0)
	    {
	        swap(str[index],str[index-1]);
	        int val=SetKT(str);
	        if(val==goal)
		        return p.ans+1;
	        if(!vis[val])
	        {
		        node.val=val;
		        node.ans=p.ans+1;
		        q.push(node);
		        vis[val]=1;
	        }
	        swap(str[index],str[index-1]);//恢复 
	    }       
	    if(index%3<2)
	    {
	        swap(str[index],str[index+1]);
	        int val=SetKT(str);
	        if(val==goal)
		        return p.ans+1;
	        if(!vis[val])
	        {
		        node.val=val;
		        node.ans=p.ans+1;
		        q.push(node);
		        vis[val]=1;
	        }
	        swap(str[index],str[index+1]);//恢复 
	    }
	    if(index/3>0)
	    {
	        swap(str[index],str[index-3]);
	        int val=SetKT(str);
	        if(val==goal)
		        return p.ans+1;
	        if(!vis[val])
	        {
		        node.val=val;
		        node.ans=p.ans+1;
		        q.push(node);
		        vis[val]=1;
	        }
	        swap(str[index],str[index-3]);//恢复 
	    }
	    if(index/3<2)
	    {
	        swap(str[index],str[index+3]);
	        int val=SetKT(str);
	        if(val==goal)
		        return p.ans+1;
	        if(!vis[val])
	        {
		        node.val=val;
		        node.ans=p.ans+1;
		        q.push(node);
		        vis[val]=1;
	        }
	        swap(str[index],str[index+3]);//恢复 
	    }
    }
    return -1;
}

int main()
{
    char str[11],end[11];
    scanf("%s",str); 
	scanf("%s",end);
	
	Deal(str);
	Deal(end);
	
	st=SetKT(str);
	goal=SetKT(end);
	
	printf("%d\n",bfs());
    return 0;
}

    原文作者:九宫格问题
    原文地址: https://blog.csdn.net/m0_37846371/article/details/77510584
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞