Eight II HDU - 3567写题报告

题目链接

原题链接

难度分析

这题啊,这题也不是很难,总的来说就是:思维+bfs逆向路径打印+打表+康托展开
我花了两天,tle四次,mle4次然后就写出来的。写完以后神清气爽,就想说一句我(w)带(d)你(n)们(m)打(d)icpc。

题解

由于每次都是改变x的位置,而x只可能出现在方块的9个位置中,所以可以根据x的位置进行分类,,然后别打表,然后再进行映射处理,得到答案。
具体来说,就是分别以

  1. 12345678x
  2. 1234567x8
  3. 123456x78
  4. 12345x678
  5. 1234x5678
  6. 123x45678
  7. 12x345678
  8. 1x2345678
  9. 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;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值