bfs+预处理+路径回溯+字典序最小+hash

本文解析了HDU竞赛中两道关于寻找最短字典序路径的问题,通过预处理固定终点状态并使用康托展开映射的方法,有效地解决了在大量可能状态中寻找最优路径的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HDU 1043 Eight
HDU 3567-Eight II
HDU 1430 魔板
这两道题很像,在这里我们看第二道,第一道比第二道简单好多;
题目意思是给你起始和终止状态让你输出路径要求字典序最小;
X代表空的位置每次X都可以和相应的上下左右互换位置,让求最终换到终止状态的路径字典序最小;
每一次都求一遍肯定会超时,因为每一种起始状态可以对应18W多的终止状态,200次;
在这里可以看一下第三题,里面有一个很好的思想,就是当终止状态固定是,我们可以通过对终止状态预处理每次只需要回溯输出路径即可;
可以根据终止状态求出起始状态所对应的状态,再在相应的表里找到路径;由于X的位置有9种所以要分别预处理这9种状态;
还有字典序是最坑的,因为我太天真了,以为反向按照字典序最大的搜,反过来输出就是字典序最小了,但是这种思路是错的,具体的根据下面的样例自己参悟;
85472X136
37681245X
这组数据的两种不同结果:
正确的:dluurddluuldrrdlluruldrdr
错误的:dluurdluldrdruldluruldrdr
错的就是因为反向搜反向输出的结果;
用康托展开映射,注意要少用string会超时的太费时间了;

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
struct zp
{
    int Hash;//字符串对应的康拓值
    int ko;//X的位置
} w;
int HASH[9]= {1,1,2,6,24,120,720,5040,40320};
int Z=1;
int flag[9][400010][3];
int z[9][4]= {3,-1,1,-1,4,0,2,-1,5,1,-1,-1,6,-1,4,0,7,3,5,1,8,4,-1,2,-1,-1,7,3,-1,6,8,4,-1,7,-1,5};
string SS;
void get_nhash(int s)//逆康拓
{
    SS="";
    char ans[10]= {"123456789"};
    for(int i=0; i<9; i++)
    {
        int y=s/HASH[8-i];
        s%=HASH[8-i];
        int sum=-1;
        for(int j=0; j<9; j++)
        {
            if(ans[j]!='0')
                sum++;
            if(sum==y)
            {
                SS+=ans[j];
                ans[j]='0';
                break;
            }
        }
    }
}
int get_Hash()//康拓
{
    int ans=0;
    for(int i=0; i<9; i++)
    {
        int sum=0;
        for(int j=i; j<9; j++)
        {
            if(SS[j]<SS[i])
                sum++;
        }
        ans+=sum*HASH[8-i];
    }
    return ans;
}
char get_ans(int a)//路径
{
    switch(a)
    {
    case 0:
        return 'd';
        break;
    case 1:
        return 'l';
        break;
    case 2:
        return 'r';
        break;
    case 3:
        return 'u';
        break;
    }
}
void print(int v,int ko)//回溯输出路径
{
    string ans="";
    while(flag[ko][v][2]!=-10)
    {
        ans+=get_ans(flag[ko][v][1]);
        v=flag[ko][v][2];
    }
    printf("Case %d: %d\n",Z++,ans.size());
    for(int i=ans.size()-1;i>=0;i--)
        printf("%c",ans[i]);
    printf("\n");
}
void bfs(string s,int a)//预处理
{
    SS=s;
    w.Hash=get_Hash();
    w.ko=a;
    queue<zp> q;
    flag[a][w.Hash][0]=1;
    flag[a][w.Hash][1]=-5;
    flag[a][w.Hash][2]=-10;
    q.push(w);
    while(!q.empty())
    {
        zp v=q.front();
        q.pop();
        get_nhash(v.Hash);
        for(int i=0; i<4; i++)
        {
            if(z[v.ko][i]!=-1)
            {
                zp u=v;
                swap(SS[v.ko],SS[z[v.ko][i]]);
                u.Hash=get_Hash();
                swap(SS[v.ko],SS[z[v.ko][i]]);
                if(flag[a][u.Hash][0]==-1)
                {
                    flag[a][u.Hash][0]=1;//判重
                    flag[a][u.Hash][1]=i;//方向
                    flag[a][u.Hash][2]=v.Hash;//父节点
                    u.ko=z[v.ko][i];
                    q.push(u);
                }
            }
        }
    }
}
void init()//预处理的九种状态
{
    string ss[10]= {"912345678","192345678","129345678","123945678","123495678","123459678","123456978","123456798","123456789"};
    for(int i=0; i<9; i++)
    {
        bfs(ss[i],i);
    }
}
void solve(string a,string b)
{
    string c=b;
    int cont=1,ko=0;
    for(int i=0; i<9; i++)
    {
        if(a[i]=='9')
        {
            ko=i;
            continue;
        }
        for(int j=0; j<9; j++)
        {
            if(b[j]==a[i])
            {
                c[j]=cont+'0';
                cont++;
                break;
            }
        }
    }//将a固定成1-8有序的c就是b变化后的样子,X的位置不能变
    SS=c;
    int ans=get_Hash();
    print(ans,ko);
}
int main()
{
    memset(flag,-1,sizeof(flag));
    init();
    int ncase;
    scanf("%d",&ncase);
    while(ncase--)
    {
        string a,b;
        cin>>a>>b;
        for(int i=0; i<9; i++)
        {
            if(a[i]=='X')
                a[i]='9';
            if(b[i]=='X')
                b[i]='9';
        }
        solve(a,b);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值