题目链接
难度分析
这题啊,这题也不是很难,总的来说就是:思维+bfs逆向路径打印+打表+康托展开
我花了两天,tle四次,mle4次然后就写出来的。写完以后神清气爽,就想说一句我(w)带(d)你(n)们(m)打(d)icpc。
题解
由于每次都是改变x的位置,而x只可能出现在方块的9个位置中,所以可以根据x的位置进行分类,,然后别打表,然后再进行映射处理,得到答案。
具体来说,就是分别以
- 12345678x
- 1234567x8
- 123456x78
- 12345x678
- 1234x5678
- 123x45678
- 12x345678
- 1x2345678
- x12345678
这九种情况作为起始状态,计算出他们可以变换的所有状态,然后将路径记录下来。
然后经过映射处理后直接得出结果
对于样例
12X453786
12345678X
建立12x453786->12x345678的映射,那么,结果12345678X就转变成了状态12645x783
所以问题就变成了,从12x345678-》12645x783的最快转变过程是什么。
由于题目还需要字典最小,而转向的字母分别为l、r、u、d,按字典序排序后,就是一步的转向顺序,即dlru。
康托展开不懂的看这里
路径打印的优化在于每一步只需记录一个字母,即从上一步到这一步的转向,然后,打印时就反向改变一次,回到上一步,直到回溯到起始状态。
AC程序
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include <unordered_map>
using namespace std;
const string A="123456789";
short M[10][10000000];
int fact[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int canto(string s)
{
int sum=0;
for(int i=0;i<9;i++)
{
int tmp=0;
for(int j=i+1;j<9;j++)
if(s[j]<s[i]) tmp++;
sum+=tmp*fact[9-1-i];
}
return sum;
}
int dir[][2]={{1,0},{0,-1},{0,1},{-1,0}};
char dirn[]="dlru";
void BFS()
{
for(int X=0;X<9;X++)
{
M[X][canto(A)]='-';
queue<string> Q;
Q.push(A);
string fnow;
while(!Q.empty())
{
string now=Q.front();
Q.pop();
int loc=now.find(X+1+'0');
int x=loc/3;
int y=loc%3;
for(int i=0;i<4;i++)
{
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx<0 || ny<0 || nx>=3 ||ny>=3)
continue;
int floc=nx*3+ny;
fnow=now;
//交换位置
swap(fnow[loc],fnow[floc]);
if(M[X][canto(fnow)])
continue;
M[X][canto(fnow)]=dirn[i];
Q.push(fnow);
}
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
BFS();
int t;
cin>>t;
int k=1;
while (t--)
{
char s[10],s2[10];
cin>>s>>s2;
int map[10];
int X=0;
//两个for是为了映射处理
for(int i=0;i<9;i++)
{
int a;
if(s[i]=='X')
{
X=i;
a=9;
}
else
a=s[i]-'0';
map[a]=i+1;
}
string news2="";
for(int i=0;i<9;i++)
{
int a;
if(s2[i]=='X')
a=9;
else
a=s2[i]-'0';
news2.push_back(map[a]+'0');
}
//cout<<"news2 "<<news2<<endl;
string ans="",now=news2,fnow;
while(M[X][canto(now)]!='-' )
{
//cout<<M[X][canto(now)]<<" while"<<endl;
//cout<<"ans "<<ans<<endl;
int loc=now.find(X+1+'0');
int x=loc/3;
int y=loc%3;
//cout<<"loc "<<loc<<endl;
ans.push_back(M[X][canto(now)]);
for(int i=0;i<4;i++)
{
if(dirn[i]==M[X][canto(now)])//找到转向
{
if(i==0) i=3;/*进行反向改变*/
else if(i==1) i=2;
else if(i==2) i=1;
else i=0;
//cout<<"i: "<<i<<endl;
int nx=x+dir[i][0];
int ny=y+dir[i][1];
if(nx<0 || ny<0 || nx>=3 ||ny>=3)
continue;
int floc=nx*3+ny;
fnow=now;
swap(fnow[loc],fnow[floc]);
now=fnow;
break;
}
}
}
cout<<"Case "<<k++<<": "<<ans.length()<<endl;
//反向打印路径
for(int J=ans.length()-1;J>=0;J--)
cout<<ans[J];
cout<<endl;
}
return 0;
}

13万+

被折叠的 条评论
为什么被折叠?



