1028 - 搜索(3种写法) - 八数码难题(luogu P1379)

传送门

Analysis

介绍三种版本的搜索,都蛮好用的

TYPE 1 双向Bfs

通常情况下,当我们明确了初始状态和目标状态的时候双向Bfs的效率就会远高于单向Bfs
而这道题就恰好很适用
我们从初末状态同时往前Bfs,如果有一个状态它的下一个状态是被末状态搜索过(或初状态,总之这两个是被不同方向Bfs到的),那么这两个状态就是连接初末的纽带
我们的答案也就出来了(由于是Bfs(广搜),我们第一次得到的纽带就是我们的最优答案(走的步数最少))

Code

#include<bits/stdc++.h>
using namespace std;
int end=123804765,sta;
map<int,int> vis,ans;
queue<int> q;
int fx,fy,i,j,now,cur;
int a[4][4];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void solve(){
	q.push(sta);
	q.push(end);
	vis[sta]=1;vis[end]=2;
	ans[sta]=0;ans[end]=1;
	while(!q.empty()){
		now=q.front();q.pop();
		cur=now;
		for(i=3;i>=1;--i)//change numbers into matrix
		for(j=3;j>=1;--j){
			a[i][j]=now%10;now/=10;
			if(a[i][j]==0) fx=i,fy=j;
		}
		for(i=0;i<4;++i){
			now=0;
			int xx=fx+dx[i];
			int yy=fy+dy[i];
			if(xx>3||xx<=0||yy>3||yy<=0) continue;
			swap(a[xx][yy],a[fx][fy]);
			for(int k=1;k<=3;++k)//change matrix into numbers
			for(j=1;j<=3;++j){
				now=now*10+a[k][j];
			}
			if(vis[now]==vis[cur]){
				swap(a[xx][yy],a[fx][fy]);
				continue;
			}
			if(vis[now]+vis[cur]==3){//bond
				printf("%d",ans[now]+ans[cur]);
				exit(0);
			}
			vis[now]=vis[cur];
			ans[now]=ans[cur]+1;
			q.push(now);
			swap(a[xx][yy],a[fx][fy]);
		}
	}
	
}
int main(){
	scanf("%d",&sta);
	if(sta==end) return printf("0"),0;
	solve();
	return 0;
}

TPYE 2 IDA*

IDA*就是迭代加深+A*
感觉优化的力度比较大

在这里我们的A*估价函数设置为
当前状态还有多少个位置与目标状态不对应
当前步数+估价函数值>枚举的最大步数 则直接返回
当然这只是基本思路,搜索还可以有很大优化
我们在搜索中再加入最优性剪枝, 显然当前枚举下一个状态时如果回到上一个状态肯定不是最优, 所以我们在枚举下一状态时加入对这种情况的判断

Code

#include<bits/stdc++.h>
using namespace std;
int sta,end=123804765,ok=0;
int dx[4]={0,1,-1,0};//注意状态对称
int dy[4]={1,0,0,-1};
int a[4][4],ans[4][4];
int k;
bool check(){
	for(int i=1;i<=3;++i)
		for(int j=1;j<=3;++j)
			if(a[i][j]!=ans[i][j]) return 0;
	return 1;
}
bool eval(int stp){
	int num=0;
	for(int i=1;i<=3;++i)
		for(int j=1;j<=3;++j)
			if(a[i][j]!=ans[i][j])
			{
				++num;
				if(num+stp>k) return 0;
			}	
	return 1;
}
void A_star(int dep,int x,int y,int pre){
	if(dep==k) {if(check()) ok=1;return;}
	if(ok) return;
	for(int i=0;i<4;++i){
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx<=0||xx>3||yy<=0||yy>3||pre+i==3) continue;
		swap(a[xx][yy],a[x][y]);
		if(eval(dep)&&!ok) A_star(dep+1,xx,yy,i);
		swap(a[xx][yy],a[x][y]);
	}
}
int main(){
	scanf("%d",&sta);
	if(sta==end) return printf("0"),0;
	int now=sta,fx,fy;
	for(int i=3;i>=1;--i)
		for(int j=3;j>=1;--j)
		{
			a[i][j]=now%10;now/=10;
			ans[i][j]=end%10;end/=10;
			if(!a[i][j]) fx=i,fy=j;
		}
	for(k=1;;k++){//迭代的深度限制 
		A_star(0,fx,fy,-1);
		if(ok){
			printf("%d",k);
			break;
		} 
	}
	return 0;
}

TPYE 3 康托展开&A*

戳这里

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值