HDU-3567 Eight II

八数码问题,给出初始状态和结束状态,要输出最短移动路径中字典序最小的那个,输入保证有解
因为数据量比较多,不能单纯的BFS,双向BFS也不能保证字典序,这里利用了映射的思想以及打表

先以X12345678,1X2345678.....,1234567X8,12345678X为开始进行BFS,把路径分开保存好
然后将起始状态映射为以上其中的一个(X的位置一样的那个),相应的把结束状态转变过去,再寻找路径回去即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
using namespace std;
const int N=362880+10;
const int dm[]={3,-1,1,-3};
const char mop[]={'d','l','r','u'}; //这里的移动是与dm对应的 
const int ten[]={1000000000,100000000,10000000,1000000,100000,10000,1000,100,10,1};
bool vis[9][N]; //访问标记 
int f[9][N]; //保存父节点 
char op[9][N]; //保存移动 
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==1) return -1;
	if(pos%3==0&&i==2) return -1;
	if(pos<4&&i==3) return -1;
	if(pos>6&&i==0) 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));
	memset(f,-1,sizeof(f));
} 
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,int k) //检查是否访问过 
{  
	if(vis[k][code]) return false;  
	return vis[k][code]=true;
}
void bfs(int sta0,int k)
{
	queue<int> q;
	q.push(sta0);
	insert(get_code(sta0),k);
	while(!q.empty()) //BFS,打表 
	{
		int u=q.front();
		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,k))
			{
				q.push(v);
				f[k][code]=codu; //记录父节点 
				op[k][code]=mop[i]; //记录到父节点的操作 
			}
		}
	}
}
char a[10],b[10];
int m[9]; //映射用数组 
int main()
{
	init();
	int sta0=12345678,sta; //全部入队 
	bfs(sta0,0);
	sta0=102345678;
	bfs(sta0,1);
	sta0=120345678;
	bfs(sta0,2);
	sta0=123045678;
	bfs(sta0,3);
	sta0=123405678;
	bfs(sta0,4);
	sta0=123450678;
	bfs(sta0,5);
	sta0=123456078;
	bfs(sta0,6);
	sta0=123456708;
	bfs(sta0,7);
	sta0=123456780;
	bfs(sta0,8);
	int T,kase=0;
	scanf("%d",&T);
	while(T--)
	{
		sta=sta0=0;
		int t=1,k;
		m[0]=0;
		scanf("%s",a);
		for(int i=0;i<9;i++)
		if(a[i]!='X') m[a[i]-'0']=t++; //映射 
		else k=i;
		scanf("%s",b);
		for(int i=0;i<9;i++)
		{
			sta*=10;
			if(b[i]!='X') sta+=m[b[i]-'0']; //转化后的状态 
		}
		int code=get_code(sta),cnt=0;;
		stack<char> s;
		while(f[k][code]!=-1) //寻找父节点,将移动操作入栈 
		{
			s.push(op[k][code]);
			code=f[k][code];
			cnt++;
		}
		printf("Case %d: %d\n",++kase,cnt); //输出 
		while(!s.empty())
		{
			printf("%c",s.top());
			s.pop();		
		}
		printf("\n");
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值