HDU 3567 Eight II 八数码(2)

Eight-puzzle, which is also called "Nine grids", comes from an old game. 

In this game, you are given a 3 by 3 board and 8 tiles. The tiles are numbered from 1 to 8 and each covers a grid. As you see, there is a blank grid which can be represented as an 'X'. Tiles in grids having a common edge with the blank grid can be moved into that blank grid. This operation leads to an exchange of 'X' with one tile. 

We use the symbol 'r' to represent exchanging 'X' with the tile on its right side, and 'l' for the left side, 'u' for the one above it, 'd' for the one below it. 

 

A state of the board can be represented by a string S using the rule showed below. 

 

The problem is to operate an operation list of 'r', 'u', 'l', 'd' to turn the state of the board from state A to state B. You are required to find the result which meets the following constrains: 
1. It is of minimum length among all possible solutions. 
2. It is the lexicographically smallest one of all solutions of minimum length. 
Input
The first line is T (T <= 200), which means the number of test cases of this problem. 

The input of each test case consists of two lines with state A occupying the first line and state B on the second line. 
It is guaranteed that there is an available solution from state A to B. 
Output
For each test case two lines are expected. 

The first line is in the format of "Case x: d", in which x is the case number counted from one, d is the minimum length of operation list you need to turn A to B.
S is the operation list meeting the constraints and it should be showed on the second line. 
Sample Input
2
12X453786
12345678X
564178X23
7568X4123
Sample Output
Case 1: 2
dd
Case 2: 8
urrulldr


题意:8数码的进阶版,这次是从A状态到达B状态确保一定有解。

嗯,表示A*算法过不了这题的,估计是数据比较多的那种。

注意:上一次的8数码的问题,只是要找到一个解就可以,所以A*算法的优化有两个权值函数h和g。并不影响最后的结果。因为只需要最快找到解就可以。

但是这一次不一样,你需要找到一个步数最少的解,你可以理解为g最小,然后还是要字典序最小的,你只需要调整一下搜索顺序就可以了。

附上一下A*这里启发式函数用的曼哈顿距离。

#include <cstdio>
#include <queue>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>

using namespace std;
const int inf = 1e8;
const int MAXN = 4e5;
int n,m;
int temp[10];
int x[10],y[10];
int aim;
char step[4] = {'d','l','r','u'};
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
int fac[9]= {1,1,2,6,24,120,720,5040,40320}; //康拖展开判重
int vis[MAXN];
int pre[MAXN];
//得到状态
int get_state(int *s)
{
    int sum=0;
    for(int i=0; i<9; i++)
    {
        int num=0;
        for(int j=i+1; j<9; j++)
            if(s[j]<s[i])num++;
        sum+=(num*fac[8-i]);
    }
    return sum;
}
//得到估价
int get_h(int *s)
{
    int ans = 0;
    for(int i = 0 ; i < 3 ; ++i)
        for(int j = 0 ; j < 3 ; ++j)
            {
                int pos = i*3+j;
                ans+=abs(i - x[s[pos]]/3)+abs(j - y[s[pos]]%3);
            }
    return ans;
}

struct node
{
    int num[9];//8数码
    int state;//状态
    int pos;//0 x的位置

    int g,h;//g表示已经花费的代价,h是估计剩余要花的代价  很明显这东西也就是这种顺序好的才能用上啊
    int f; //f = g+h;表示总估价 按照从小到大排序。
    bool operator < (const node &a)const
    {
        return f>a.f;
    }
};

void bfs(node x)
{
    memset(vis,-1,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    priority_queue<node>q;
    q.push(x);
    vis[x.state] = 1;
    while(!q.empty())
    {
        node u = q.top();
        q.pop();
        if(u.state == aim)
        {
            string ans;
            while(u.state != -1)
            {
                ans += step[vis[u.state]];
                u.state = pre[u.state];
            }
            printf("%d\n",ans.size()-1);
            for(int i = ans.size()-2 ; i >= 0 ; --i)printf("%c",ans[i]);
            puts("");
            return ;
        }
        //4种交换
        int x = u.pos/3;
        int y = u.pos%3;
        for(int i = 0 ; i < 4; ++i)
        {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3)
            {
                node v = u;
                swap(v.num[u.pos],v.num[nx*3+ny]);
                v.state = get_state(v.num);

                if(vis[v.state] == -1)
                {
                    v.pos = nx*3+ny;
                    v.g++;
                    v.h = get_h(v.num);
                    v.f = v.g + v.h;

                    vis[v.state] = i;
                    pre[v.state] = u.state;
                    q.push(v);
                }
            }
        }
    }
}

int main()
{
    int t,ca = 0;
    char s[15];
    scanf("%d",&t);
    while(t--)
    {
        node u;
        scanf("%s",s);
        for(int i = 0 ; i < 9; ++i)
        {
            if(s[i] == 'X')
            {
                u.num[i] = 0;
                u.pos = i;
            }
            else u.num[i] = s[i] - '0' ;
        }
        u.state = get_state(u.num);
        u.g = 0;
        u.h = get_h(u.num);
        u.f = u.g + u.h;

        scanf("%s",s);
        for(int i = 0 ; i < 9 ; ++i)
        {
            if(s[i] == 'X')temp[i] = 0;
            else temp[i] = s[i] - '0' ;
        }
        aim = get_state(temp);
        for(int i = 0 ;i < 9 ; ++i)
        {
            x[temp[i]] = i/3;
            y[temp[i]] = i%3;
        }
        ca++;
        printf("Case %d: ",ca);
        bfs(u);
    }
    return 0;
}

然而是TLE的,可能是组数比较多的那种。上面一个题也不难想到一种预处理的办法,但是内存限制了一半。

然后这个题采用预处理的办法用了33196KB。所以上一题预处理会超内存。

这个题A*不可过,预处理则可以。不过估计双向bfs(划掉)也可破。字典序啊喂,双向bfs根本无法控制的好吧。什么可破啊,我试了一晚上最终发现这根本不可控QAQ。因为前半字典序最小+后半字典序最小不等于前半加后半字典序最小!!!!!!!譬如说aaaaab和aaabba的例子

你会输出aaabba,实际上最好的则是aaaaab。


预处理:这里你需要知道其实每个串无非就9种情况,只与X的位置有关,其余的你可以直接做一个映射关系。

char eight_num[9][10] = {"X12345678" , "1X2345678" ,  "12X345678" , "123X45678" ,
                        "1234X5678" , "12345X678" , "123456X78" , "1234567X8" , "12345678X" };

这里就是基本的9种情况,然后我们做一个映射就可以了。

比如说示例1中的

12X453786
我们就可以做这样的一个映射“12X345678”

分别是:1 → 1,2 → 2,X → X,4 → 3,5 → 4,3 → 5,7 → 6,8 → 7,6 → 8,

这样我们只需要搜索8种基本状态记录路径就可以了。

然后目标状态一样要映射掉。

这样就可以得到答案了。

#include <cstdio>
#include <queue>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>

using namespace std;
const int inf = 1e8;
const int MAXN = 4e5;
int n,m;
char eight_num[9][10] = {"X12345678" , "1X2345678" ,  "12X345678" , "123X45678" ,
                        "1234X5678" , "12345X678" , "123456X78" , "1234567X8" , "12345678X" };


char step[4] = {'d','l','r','u'};
int dx[4] = {1,0,0,-1};
int dy[4] = {0,-1,1,0};
int fac[9]= {1,1,2,6,24,120,720,5040,40320}; //康拖展开判重
int vis[9][MAXN];
int pre[9][MAXN];
//得到状态
int get_state(int *s)
{
    int sum=0;
    for(int i=0; i<9; i++)
    {
        int num=0;
        for(int j=i+1; j<9; j++)
            if(s[j]<s[i])num++;
        sum+=(num*fac[8-i]);
    }
    return sum;
}


struct node
{
    int num[9];//8数码
    int state;//状态
    int pos;//0 x的位置

};

void print(int state,int kind)//输出目标状态的答案
{
    string ans;
    while(state != -1)
    {
        ans += step[vis[kind][state]];
        state = pre[kind][state];
    }
    printf("%d\n",ans.size()-1);
    for(int i = ans.size()-2 ; i >= 0 ; --i)printf("%c",ans[i]);
    puts("");
}

void bfs(node x,int kind)
{
    queue<node>q;
    q.push(x);
    vis[kind][x.state] = 1;
    while(!q.empty())
    {
        node u = q.front();
        q.pop();

        //4种交换
        int x = u.pos/3;
        int y = u.pos%3;
        for(int i = 0 ; i < 4; ++i)
        {
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3)
            {
                node v = u;
                swap(v.num[u.pos],v.num[nx*3+ny]);
                v.state = get_state(v.num);

                if(vis[kind][v.state] == -1)
                {
                    v.pos = nx*3+ny ;
                    vis[kind][v.state] = i;
                    pre[kind][v.state] = u.state;
                    q.push(v);
                }
            }
        }
    }
}

void init(char *s,int kind)//预处理
{
    node u;
    for(int i = 0 ; i < 9; ++i)
    {
        if(s[i] == 'X')
        {
            u.num[i] = 0;
            u.pos = i;
        }
        else u.num[i] = s[i] - '0' ;
    }
    u.state = get_state(u.num);
    bfs(u,kind);
}
int main()
{
    int t,ca = 0;
    char s[15],c[15];
    int temp[MAXN];
    int kind;
    memset(vis,-1,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for(int i = 0 ;i < 9 ; ++i)init(eight_num[i],i);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%s",s);
        int cnt = 1;
        //设置映射初始状态8数码
        for(int i = 0 ;i < 9 ; ++i)
        {
            if(s[i] == 'X')
            {
                c[0] = 0;
                kind = i;
            }
            else c[s[i] - '0'] = cnt++;
        }
        //映射目标8数码
        scanf("%s",s);
        for(int i = 0 ; i < 9 ; ++i)
        {
            if(s[i] == 'X')temp[i] = c[0];
            else temp[i] = c[s[i] - '0'] ;
        }

        int aim = get_state(temp);//得到目标状态
        ca++;
        printf("Case %d: ",ca);
        print(aim,kind);
    }
    return 0;
}








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值