HDU-1043 Eight

这道题刚开始单纯的用BFS做,从初始状态搜到12345678x,因为有很多组输入,妥妥的TLE
后来试了下双向BFS,还是TLE,(没做好优化,优化好的话是可以卡时间过去的)
最好最简单的解法是,从12345678x开始搜索,打表记录路径和移动,这样搜索一次,就把能到达的状态还有路径全部记录下来了,再输入时直接查询输出就可以,很快
状态压缩的话需要用一种编码方式,将012345678的全排列对应为0~362880之间的数,具体操作看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int dm[]={-3,3,-1,1};
const char mop[]={'u','d','l','r'}; //这里的移动是与dm对应的 
const char mop0[]={'d','u','r','l'}; //这里是反向的移动,用来保存打表 
const int ten[]={1000000000,100000000,10000000,1000000,100000,10000,1000,100,10,1};
bool vis[362880]; //访问标记 
int f[362880]; //保存父节点 
char op[362880]; //保存移动 
int move(int n,int i) //移动 
{
	int pos=9,tmp=n;
	while(pos>1)
	{
		if(n%10==0) break;
		n/=10;
		pos--;
	}
	if(pos%3==1&&i==2) return -1;
	if(pos%3==0&&i==3) return -1;
	if(pos<4&&i==0) return -1;
	if(pos>6&&i==1) return -1;
	int np=pos+dm[i];
	int t=tmp%ten[np-1]/ten[np];
	tmp+=ten[pos]*t;
	tmp-=ten[np]*t;
	return tmp;
}
int fact[9]; //编码用 
void init() //初始化 
{  
	fact[0]=1;  
	for(int i=1;i<9;i++) 
	fact[i]=fact[i-1]*i;
	memset(vis,false,sizeof(vis));
} 
int get_code(int s) //转码 
{
	int code=0;
	int st[9];
	for(int i=8;i>=0;i--,s/=10) 
	st[i]=s%10; 
	for(int i=0;i<9;i++)
	{    
		int cnt=0;    
		for(int j=i+1;j<9;j++) 
		if(st[j]<st[i]) cnt++;    
		code+=fact[8-i]*cnt;  
	}  
	return code;
}
bool insert(int code) //检查是否访问过 
{  
	if(vis[code]) return false;  
	return vis[code]=true;
}
int sta;
bool input() //输入 
{
	char a[10];
	bool flag=false;
	int i=0;
	sta=0;
	while(~scanf("%s",a))
	{
		sta*=10;
		if(a[0]!='x')
		sta+=a[0]-'0';
		i++;
		if(i==9) break;
	}
	if(i==9)
	flag=true;
	return flag;
}
int main()
{
	int sta0=123456780;
	init();
	queue<int> q;
	q.push(sta0);
	insert(get_code(sta0));
	memset(f,-1,sizeof(f));
	while(!q.empty()) //从初始状态开始BFS,打表 
	{
		int u=q.front();//cout<<1<<" "<<u<<endl;
		q.pop();
		int codu=get_code(u);
		for(int i=0;i<4;i++)
		{
			int v=move(u,i);
			if(v==-1) continue;
			int code=get_code(v);
			if(insert(code))
			{
				q.push(v);
				f[code]=codu; //记录父节点 
				op[code]=mop0[i]; //记录到父节点的操作 
			}
		}
	}
	while(input())
	{
		int code=get_code(sta);
		queue<char> s0;
		if(vis[code]) //被访问过,可以到达 
		{
			while(f[code]!=-1) //寻找父节点,输出移动操作 
			{
				printf("%c",op[code]);
				code=f[code];
			}
		}
		else printf("unsolvable");
		printf("\n");
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值