hdu1043 Eight(搜索/八数码问题)

题目

给你一个1-8的字符串和一个位置x,如13264578x

代表

1 3 2 

6 4 5   

7 8 x

要求将状态通过重复x与相邻位置交换过程,将局面复位为

1 2 3 

4 5 6 

7 8 x

要求在最少步数的情况下,输出路径

x向上走则为u,向下d,向左l,向右r

思路来源

https://www.cnblogs.com/goodness/archive/2010/05/04/1727141.html

http://www.bubuko.com/infodetail-635279.html(八数码的八境界)

题解

令x的位置=9,1-9共用9!=36W种状态,康托展开压进36W数组,

从123456789终态逆向bfs,预处理每个状态的后继状态是什么字母,

注意字母应反向,如cantor(123456789)通过l到达cantor(123456798)

则记pre[cantor(123456798)]=cantor(123456789);ans[cantor(123456798)=l;

在询问的时候沿着pre跳就好了,对于有解的情况不过20步左右

朴素的康托展开O(9*9),乘以36W状态,不会超时

心得

去年10月份的大一作业,现在才补上,

因为这个学了康托展开、IDA*入门和A*入门

后来发现后二者并没什么用,并不怎么会用到

八数码的八境界,我大概只能达到第二三重吧,补上就好了

代码

#include <iostream>
#include <algorithm> 
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const double INF=0x3f3f3f3f;
const int maxn=370005; 
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int> 
#define si set<int>
#define pii pair<int,int> 
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a)) 
using namespace std;
int jc[10],Hash[maxn],ans[10],endv;
int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
int pre[maxn];
char res[maxn];
char query[105];
char dir[5]="uldr";//由逆操作,记录(x+dx,y+dy)的反方向 
struct node
{
	int s[9];//记录每个位置的值 
	int pos;//记录x的位置,也就是9的位置 
	int v;//记录hash值
};
queue<node>q;
void init()
{
	jc[0]=1;
	rep(i,1,9)jc[i]=i*jc[i-1];
}
int cantor(int s[])
{ 
   int sum=0,len=9;
   rep(i,0,len-1)
   {
   	int num=0;
   	rep(j,i+1,len-1)
   	{
   		if(s[i]>s[j])num++;//后面有多少个数比s[i]小 
   	}
   	sum+=num*jc[len-1-i]; 
   }	
   return sum;
}
bool check(int x,int y)
{
	if(x>=0&&x<=2&&y>=0&&y<=2)return 1;
	return 0;
}
void bfs()
{
	node tmp,now;
	rep(i,0,8)tmp.s[i]=i+1;
	tmp.pos=8;
	tmp.v=cantor(tmp.s);
	endv=tmp.v;
	q.push(tmp);
	while(!q.empty())
	{
		tmp=q.front();
		q.pop();
		int x=tmp.pos/3,y=tmp.pos%3;
		rep(i,0,3)
		{
			int xx=x+dx[i],yy=y+dy[i];
			if(check(xx,yy))
			{
				now=tmp;
				now.pos=xx*3+yy;
				//把(x,y)和(xx,yy)互换
				now.s[tmp.pos]=now.s[now.pos];//将原x处置现x数 
				now.s[now.pos]=9;//将现x数置x 
				now.v=cantor(now.s);
				if(!Hash[now.v])
				{
					Hash[now.v]=1;
					pre[now.v]=tmp.v;
					res[now.v]=dir[i];
					q.push(now);
				} 
			}
		}
	}
} 
int main()
{
   init();
   bfs();
   while(gets(query))
   {
   	 mem(ans,0);
   	 int len=strlen(query);
   	 int sum=0,cnt=0;
   	 rep(i,0,len-1)
   	 {
   	 	if(query[i]>='1'&&query[i]<='8')ans[cnt++]=query[i]-'0';
   	 	else if(query[i]=='x')ans[cnt++]=9;
   	 }
   	 int t=cantor(ans);
   	 if(Hash[t])
   	 {
   	 	for(int i=t;i!=endv;i=pre[i])
   	 	putchar(res[i]);
   	 	puts("");
   	 }
   	 else puts("unsolvable");
   }
   return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值