kuangbin题单 进阶搜素 反向广搜 康托展开 哈希 Eight HDU1043

本文介绍了一种使用康托展开和逆向BFS算法解决3x3数字华容道问题的方法。通过康托展开确定排列名次,并利用逆向BFS搜索从目标状态回到起始状态的路径,实现对多种输入的有效处理。

打卡 day3
这是一个3X3的数字华容道,我一开始也是毫无头绪,然后去看佬们的博客。
首先是康托展开
感觉看进去之后很简单,对应到这道题上就是把1~8进行全排列,应用康托展开能确定当前的排列名次,先看比首位小的数有几个,记作a,然后再看第二位,记作b…注意已经用过的数不计入后面位数数字的统计中(比如3412,比3小的有1,2,记a=2,比4小的有1,2,3,但之前已经出现过3,所以只考虑1,2,记b=2…)
最后由公式可得X=a·7!+b·6!+c·5!+d·4!+e·3!+f·2!+g·1!+h·0!
我们可以说,当前的排列之前有X位,当前的排列排在第X+1位。
康托和逆康托就当成板子用吧,思路是知道了。
这道题就是存储每个时刻的顺序用康托存了起来,表示一种情况,实现hash存储。逆bfs也就是从最终结果({1,2,3},{4,5,6},{7,8,x})出发,直到搜到输入的样例康托值为止,这样做是因为多组输入,预先把答案都存到了path字符串数组中。

#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=362880+10;
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};
struct node{
	string path;
	int hash;
	int pos;
}now,st;
queue<node> q;
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
char opp[5]={'u','d','l','r'};
int temp[9];
int result=46234;
string path[maxn];
int vis[maxn];
int cantor(int *a){
	int x=0;
	for(int i=0;i<9;i++){
		int smaller=0;
		for(int j=i+1;j<9;j++){
			if(a[j]<a[i]) smaller++;
		}
		x+=fac[9-i-1]*smaller;
	}
	return x+1;
}
void decantor(int x,int *a){
	vector<int> v;
	for(int i=0;i<9;i++) v.push_back(i);
	for(int i=0;i<9;i++){
		int r=x%fac[9-i-1];
		int t=x/fac[9-i-1];
		x=r;
		sort(v.begin(),v.end());
		a[i]=v[t];
		v.erase(v.begin()+t);
	}
}
void bfs(){
	memset(vis,0,sizeof(vis));
	for(int i=0;i<8;i++){
		temp[i]=i+1;
	}
	temp[8]=0;
	now.pos=8;
	now.hash=result;
	now.path="";
	vis[result]=1;
	path[result]="";
	q.push(now);
	while(!q.empty()){
		now=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			int xx=now.pos/3+dir[i][0];
			int yy=now.pos%3+dir[i][1];
			if(xx>=0&&yy>=0&&xx<=2&&yy<=2){
				st=now;
				st.pos=xx*3+yy;
				decantor(now.hash-1,temp);
				swap(temp[now.pos],temp[st.pos]);
				st.hash=cantor(temp);
				if(!vis[st.hash]){
					vis[st.hash]=1;
					st.path=opp[i]+st.path;
					q.push(st);
					path[st.hash]=st.path;
				}
			}
		}
	}
	return ;
}
int main(){
	bfs();
	char x;
	while(cin>>x){
		if(x=='x'){
			//now.pos=0;
			temp[0]=0;
		}else{
			temp[0]=x-'0';
		}
		for(int i=1;i<9;i++){
			cin>>x;
			if(x=='x'){
				//now.pos=i;
				temp[i]=0;
			}else{
				temp[i]=x-'0';
			}
		}
		now.hash=cantor(temp);
		if(!vis[now.hash]) cout<<"unsolvable\n";
		else cout<<path[now.hash]<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值