蓝桥杯OJ PREV-19 九宫重排 BFS+hash判重

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

  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
  输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
  输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
样例输入
13524678.
46758123.
样例输出
22

以前在POJ上看过这道题的加强版,没去写 囧rz  看到第二组样例,写普通BFS显然作死。不加重复判断MLE,加重复判断TLE。可是我还是不信邪的拍了个裸BFS交了上去

//样例跑了5分钟,喜闻乐见
#include <iostream>
using namespace std;
const int dx[4]={ 1,-1, 0, 0};
const int dy[4]={ 0, 0, 1,-1};
const int MAX=362280;
struct node{
    int a[3][3],x,y,step;
}queue[MAX]={0},end,temp;
bool equal(node x,node y){
    for (int i=0;i<3;i++)
        for (int j=0;j<3;++j)
            if (x.a[i][j]!=y.a[i][j]) 
                return false;
    return true;
}
bool check(int x){
    for (int i=0;i<=x;++i)
        if (equal(queue[i],temp)) return false;
    return true;
}
int i,j,k,fa,so; string s;
int main()
{
    cin>>s;
    for (i=0;i<3;++i)
        for (j=0;j<3;++j)
            if (s[i*3+j]>48)
                queue[0].a[i][j]=s[i*3+j]-48;
            else{
                queue[0].x=i; queue[0].y=j;
                queue[0].a[i][j]=0;
            }
    queue[0].step=0;
    cin>>s;
    for (i=0;i<3;++i)
        for (j=0;j<3;++j)
            if (s[i*3+j]>48)
                end.a[i][j]=s[i*3+j]-48;
            else{
                end.x=i; end.y=j;
                end.a[i][j]=0;
            }
    fa=so=0;
    while (fa<=so && so<MAX){
        for (i=0;i<4;++i)
            if (queue[fa].x+dx[i]>=0 && queue[fa].x+dx[i]<3 && queue[fa].y+dy[i]>=0 && queue[fa].y+dy[i]<3){
                temp=queue[fa];
                swap(temp.a[temp.x][temp.y],temp.a[temp.x+dx[i]][temp.y+dy[i]]);
                temp.x+=dx[i]; temp.y+=dy[i]; temp.step++;
                if (equal(temp,end)){
                    cout<<temp.step<<endl;
                    return 0;
                }
                if (check(so)) queue[++so]=temp;
            }
        ++fa;
    }
    cout<<"-1\n";
    return 0;
}

以前看到过有各种搜索优化。不过这道题的时间消耗瓶颈是判重(时间复杂度增加了一个次方)使用hash的话。可以让判重的时间复杂度降至O(1)。整体时间复杂度也是常数(队列长3*10^6) 


//一个略靠谱的hash判重 
#include <iostream>
using namespace std;

const int dx[4]={ 1,-1, 0, 0};
const int dy[4]={ 0, 0, 1,-1};
const int MAX=362280,MAXH=999983;

struct node{
	int a[3][3],x,y,step;
}queue[MAX]={0},end,temp;

int t,i,j,k,fa,so; string s;
int hash[MAXH]={0};

bool equal(node x,node y){
	for (int i=0;i<3;i++)
		for (int j=0;j<3;++j)
			if (x.a[i][j]!=y.a[i][j]) 
				return false;
	return true;
} 

int Hash(node x){
	int k=1;
	t=0;
	for (int i=0;i<3;++i)
		for (int j=0;j<3;++j){
			t+=x.a[i][j]*k;
			k*=10;
		}
	k=t%MAXH;
	while (hash[k] && hash[k]!=t) k=(k+1)%MAXH;
	return k; 
}

int main()
{
	cin>>s;
	for (i=0;i<3;++i)
		for (j=0;j<3;++j)
			if (s[i*3+j]>48)
				queue[0].a[i][j]=s[i*3+j]-48;
			else{
				queue[0].x=i; queue[0].y=j;
				queue[0].a[i][j]=0;
			}
	queue[0].step=0;
	cin>>s;
	for (i=0;i<3;++i)
		for (j=0;j<3;++j)
			if (s[i*3+j]>48)
				end.a[i][j]=s[i*3+j]-48;
			else{
				end.x=i; end.y=j;
				end.a[i][j]=0;
			}
	fa=so=0;
	while (fa<=so && so<MAX){
		for (i=0;i<4;++i)
			if (queue[fa].x+dx[i]>=0 && queue[fa].x+dx[i]<3 && queue[fa].y+dy[i]>=0 && queue[fa].y+dy[i]<3){
				temp=queue[fa];
				swap(temp.a[temp.x][temp.y],temp.a[temp.x+dx[i]][temp.y+dy[i]]);
				temp.x+=dx[i]; temp.y+=dy[i]; temp.step++;
				if (equal(temp,end)){
					cout<<temp.step<<endl;
					return 0;
				}
				int flag=Hash(temp);
				if (hash[flag]==0){
					hash[flag]=t;
					queue[++so]=temp;
				}
			}
		++fa;
	}
	cout<<"-1\n";
	return 0;
}

kdwycz的网站:  http://kdwycz.com/

kdwyz的刷题空间:http://blog.csdn.net/kdwycz



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值