POJ 1077(HDU 1043) Eight(八数码A*算法)

通过演算发现每次一动,逆序对增加一定是偶数对,如果出现奇数则肯定不符合要求,所以要事先判断否则会超时。奇偶剪枝是一种非常常用的剪枝方法,如果在搜索中超时,可以尝试奇偶剪枝。

八数码问题中是0~8的9个数字,如果当做排列是不能算0的,相当于1~8的8位数字的排列,当左右移动时,1~8的排列,没有改变,当上下移动时,上下的数字走到0的位置都要经过2次的交换,所以交换是偶数次的,那么不改变排列的奇偶性,1~8的标准排列逆序对是0,那么标准排列是偶排列,如果八数码排列是奇排列,那么直接剪枝。

//
//  main.cpp
//  Richard
//
//  Created by 邵金杰 on 16/8/24.
//  Copyright © 2016年 邵金杰. All rights reserved.
//



#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=400000;
struct node{
    int sta[9];
    int f,g,h;
    int pos;
    int hash;
    bool operator < (const node &e) const {
        return f>e.f;
    }
};
int vis[maxn];
int End[9]={1,2,3,4,5,6,7,8,0};
int ha[9]={1,1,2,6,24,120,720,5040,40320};
struct pre{
    int pre;
    char ch;
}pr[maxn];
int get_hash(int a[9])
{
    int res=0;
    for(int i=0;i<9;i++)
    {
        int k=0;
        for(int j=i+1;j<9;j++)
        {
            if(a[j]<a[i]) k++;
        }
        res+=k*ha[8-i];
    }
    return res;
}
int get_h(int a[9])
{
    int dist=0;
    for(int i=0;i<9;i++)
    {
        int x1=i/3,y1=i%3;
        int x2,y2;
        for(int j=0;j<9;j++)
        {
            if(End[j]==a[i]) {x2=j/3;y2=j%3;break;}
        }
        dist+=(x1-x2>0?x1-x2:-(x1-x2));
        dist+=(y1-y2>0?y1-y1:-(y1-y2));
    }
    return dist;
}
bool reverse(node s)
{
    int times=0;
    for(int i=0;i<9;i++)
    {
        if(s.sta[i]==0) continue;
        for(int j=i+1;j<9;j++)
        {
            if(s.sta[j]==0) continue;
            if(s.sta[i]>s.sta[j]) times++;
        }
    }
    return times&1;
}
bool inside(int x,int y)
{
    if(x>=0&&x<=2&&y>=0&&y<=2) return true;
    return false;
}
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
char d[5]="lurd";
void swap(int *a,int pos1,int pos2)
{
    int temp=a[pos1];
    a[pos1]=a[pos2];
    a[pos2]=temp;
}
void print(int x)
{
    if(pr[x].pre==-1) return ;
    print(pr[x].pre);
    printf("%c",pr[x].ch);
}
void Astar(node s)
{
    int end_hash=get_hash(End);
    if(end_hash==s.hash) {printf("\n");return ;}
    memset(vis,0,sizeof(vis));
    vis[s.hash]=1;
    pr[s.hash].pre=-1;
    priority_queue<node> pq;
    pq.push(s);
    node p,q;
    while(!pq.empty())
    {
        p=pq.top();
        pq.pop();
        int x=p.pos/3,y=p.pos%3;
        for(int i=0;i<4;i++)
        {
            int nx=x+dx[i];
            int ny=y+dy[i];
            if(!inside(nx,ny)) continue;
            q=p;
            swap(q.sta,x*3+y,nx*3+ny);
            q.hash=get_hash(q.sta);
            if(vis[q.hash]) continue;
            vis[q.hash]=1;
            q.pos=nx*3+ny;
            q.g++;
            q.h=get_h(q.sta);
            q.f=q.g+q.h;
            pr[q.hash].pre=p.hash;
            pr[q.hash].ch=d[i];
            if(q.hash==end_hash) {print(end_hash);printf("\n");return ;}
            pq.push(q);
        }
    }
}
int main()
{
    char str[100];
    while(gets(str))
    {
        int len=(int)strlen(str);
        node s;
        int k=0;
        int flag=0;
        for(int i=0;i<len;i++)
        {
            if(str[i]==' ') continue;
            if(str[i]=='x') {flag=k;s.sta[k++]=0;}
            else s.sta[k++]=str[i]-'0';
        }
        if(reverse(s))
        {
            printf("unsolvable\n");
            continue;
        }
        s.g=0;
        s.h=get_h(s.sta);
        s.f=s.g+s.h;
        s.hash=get_hash(s.sta);
        s.pos=flag;
        Astar(s);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值