八数码难题codevs1225

题目描述 Description

Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.

问题描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入描述 Input Description

输入初试状态,一行九个数字,空格用0表示

输出描述 Output Description

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

样例输入 Sample Input

283104765

样例输出 Sample Output

4

数据范围及提示 Data Size & Hint

详见试题

注意 :map会超时(我指的是单向bfs);

新东西,康拓展开;

ll cantor(hh xs)
{
    ll num=0;
    for(ll i=1;i<=9;i++)
    {
        ll tmp=0;
        for(ll j=i+1;j<=9;j++)
            if(xs.a[i]>xs.a[j]) tmp++;
        num+=tmp*fac[9-i];
    }
    return num;
}

把一个整数X展开成如下形式:
X=a[n](n-1)!+a[n-1](n-2)!+…+a[i]*(i-1)!+…+a[2]*1!+a[1]*0!
其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
char s[10];
typedef unsigned long long ll;
bool vis[10000001];
ll fac[10]={1,1,2,6,24,120,720,5040,40320,362880},la,t;
ll X[]={0,1,-1,0,0};
ll Y[]={0,0,0,-1,1};
struct hh
{
    ll x,y,a[10],step;
};
ll cantor(hh xs)
{
    ll num=0;
    for(ll i=1;i<=9;i++)
    {
        ll tmp=0;
        for(ll j=i+1;j<=9;j++)
            if(xs.a[i]>xs.a[j]) tmp++;
        num+=tmp*fac[9-i];
    }
    return num;
}
queue<hh>q;
hh start,js,now,u;
bool can(ll fx,ll fy)
{
    if(fx>=1 && fx<=3 && fy>=1 && fy<=3) return true;
    return false;
}
void bfs()
{
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        if(cantor(u)==la) {cout<<u.step<<endl;return;}
        for(ll i=1;i<=4;i++)
        {
            now=u;
            now.x=X[i]+u.x;
            now.y=Y[i]+u.y;
            if(can(now.x,now.y))
            {
                now.a[u.x*3+u.y-3]=now.a[now.x*3+now.y-3];
                now.a[now.x*3+now.y-3]=0;
                now.step=u.step+1;
                if(!vis[cantor(now)]) vis[cantor(now)]=1,q.push(now);
                if(cantor(now)==la) {cout<<now.step<<endl;return;}
            }
        }
    }
    return;
}
void init()
{
    for(ll i=1;i<=9;i++) start.a[i]=s[i]-'0';
    for(ll i=1;i<=9;i++)
    {
        if(start.a[i]==0)
        {
            if(i==1) start.x=1,start.y=1;
            else if(i==2) start.x=1,start.y=2;
            else if(i==3) start.x=1,start.y=3;
            else if(i==4) start.x=2,start.y=1;
            else if(i==5) start.x=2,start.y=2;
            else if(i==6) start.x=2,start.y=3;
            else if(i==7) start.x=3,start.y=1;
            else if(i==8) start.x=3,start.y=2;
            else if(i==9) start.x=3,start.y=3;
            break;
        }
    }
    start.step=0;
    vis[cantor(start)]=1;
    q.push(start);
    js.a[1]=1,js.a[2]=2,js.a[3]=3,js.a[4]=8,js.a[5]=0;
    js.a[6]=4,js.a[7]=7,js.a[8]=6,js.a[9]=5;
    la=cantor(js);
    return;
}
void solve()
{
    cin>>s+1;
    init();
    bfs();
    return;
}
int main()
{
    solve();
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值