八数码问题(九宫格重排) 利用康托展开 判重
问题 E: [蓝桥杯][历届试题]九宫重排
时间限制: 1Sec 内存限制: 128MB 提交: 69 解决: 17
题目描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出
输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
#include<bits/stdc++.h>
using namespace std;
string s,d;
bool vis[400000];
int FAC[11];
void init()
{
FAC[0]=1;
for(int i=1;i<=10;i++)FAC[i]=FAC[i-1]*i;
}
int cantor(string a, int n)
{
int x = 0;
for (int i = 0; i < n; ++i) {
int smaller = 0; // 在当前位之后小于其的个数
for (int j = i + 1; j < n; ++j) {
if (a[j] < a[i])
smaller++;
}
x += FAC[n - i - 1] * smaller; // 康托展开累加
}
return x+1; // 康托展开值
}
int ans=-1;
void bfs()
{
int dx[4]={1,0,-1,0};
int dy[4]={0,1,0,-1};
queue<string>q;
string t=s;
q.push(t);
q.push("");
vis[cantor(t,9)]=1;
int depth=0;int x,y;int x2,y2;int newpos;
while(!q.empty())
{
t=q.front();q.pop();
if(t==""){depth++;q.push("");continue;}
if(t==d){ans=depth;break;}
else
{
int pos=0;
while(t[pos]!='0')pos++;
pos++;
x=(pos+2)/3;
y=(pos-1)%3+1;
for(int i=0;i<4;i++)
{
x2=x+dx[i];y2=y+dy[i];
if(x2<1||x2>3||y2<1||y2>3)continue;
newpos=(x2-1)*3+y2;
swap(t[pos-1],t[newpos-1]);
if(!vis[cantor(t,9)]){q.push(t);vis[cantor(t,9)]=1;}
swap(t[pos-1],t[newpos-1]);
}
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
init();
cin>>s>>d;
for(int i=0;i<9;i++)if(s[i]=='.')s[i]='0';
for(int i=0;i<9;i++)if(d[i]=='.')d[i]='0';
bfs();
cout<<ans<<endl;
return 0;
}