HDU Eight II(BFS+康托展开)

20 篇文章 0 订阅
4 篇文章 0 订阅

Eight II

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 130000/65536 K (Java/Others)
Total Submission(s): 2347    Accepted Submission(s): 504


Problem Description
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
 

Author
zhymaoiing
 

Source
 

Recommend
zhouzeyong   |   We have carefully selected several similar problems for you:   3459  2918  1430  1401  2691


题目大意:

    给出八数码问题的起始状态和目标状态,输出最小步数,以及在步数最小的前提下字典序最小的路径。


解题思路:

    这题的关键就是使用映射的思想。对于八数码问题除了X之外其它的格子的移动规则没有差别。所以对于任意一个起始状态,我们都可以对那些数字重新编号,只需要把目标状态通过相同的映射进行重新编号即可。

    这样我们就可以将所有的起始状态转化成X位置不同的9种状态。于是我们可以通过bfs预处理出每一个起始状态到达每一个重点状态的最佳路径,每次出入直接找到输出即可。

    这题应该是只能用康托展开,一共有9! * 9个状态,其它方法的状态压缩紧密程度没有康托展开高,可能会开不下那么大的数组。还有这题要求字典序最小,所以只能正向搜,反向搜的话无法保证字典序。

    关于康托展开的详细介绍,推荐一个大牛的博客


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXS=362880+3;//9!
const int MAXN=9;
const int sup[]={1,1,2,6,24,120,720,5040,40320};//阶乘表,用于康托展开
int maze[MAXN];//保存棋盘状态的临时数组
string s1,s2,res;
char ans[MAXN][MAXS];//从上一个状态走到当前状态的移动方向
int path[MAXN][MAXS];//走到的下一个状态
queue<int> que;
int init_id;//最终状态id
int C,f[MAXN];

int get_id()//康托展开
{
    int res=0;
    for(int i=0;i<9;++i)
    {
        int cnt=0;//剩下中第几小
        for(int j=i+1;j<9;++j)
            if(maze[j]<maze[i])
                ++cnt;
        res+=cnt*sup[8-i];
    }
    return res;
}

void get_statue(int id)//通过康托逆展开生成状态
{
    int a[MAXN];//存剩下中第几小
    bool used[MAXN];//是否已用
    for(int i=8;i>=0;--i)
    {
        used[i]=false;
        a[8-i]=id/sup[i];
        id%=sup[i];
    }
    int cnt;
    for(int i=0;i<MAXN;++i)
    {
        cnt=0;
        for(int j=0;j<MAXN;++j)
            if(!used[j])
            {
                if(cnt==a[i])
                {
                    maze[i]=j;
                    used[j]=true;
                    break;
                }
                else ++cnt;
            }
    }
}

void init()//bfs倒推预处理出所有结果
{
    mem(path,-1);
    for(C=0;C<MAXN;++C)
    {
        int tmp=1;
        for(int i=0;i<MAXN;++i)
            maze[i]=(i==C?0:tmp++);
        init_id=get_id();
        que.push(init_id);
        while(!que.empty())
        {
            int now=que.front(); que.pop();
            get_statue(now);
            int p=-1;//x的位置
            for(int i=0;i<MAXN;++i)
                if(maze[i]==0)
                    p=i;
            if(p<6)//x下移
            {
                swap(maze[p],maze[p+3]);
                int next=get_id();
                if(next!=init_id&&path[C][next]==-1)
                {
                    path[C][next]=now;
                    ans[C][next]='d';
                    que.push(next);
                }
                swap(maze[p],maze[p+3]);
            }
            if(p!=0&&p!=3&&p!=6)//x左移
            {
                swap(maze[p],maze[p-1]);
                int next=get_id();
                if(next!=init_id&&path[C][next]==-1)//新状态
                {
                    path[C][next]=now;
                    ans[C][next]='l';
                    que.push(next);
                }
                swap(maze[p],maze[p-1]);
            }
            if(p!=2&&p!=5&&p!=8)//x右移
            {
                swap(maze[p],maze[p+1]);
                int next=get_id();
                if(next!=init_id&&path[C][next]==-1)
                {
                    path[C][next]=now;
                    ans[C][next]='r';
                    que.push(next);
                }
                swap(maze[p],maze[p+1]);
            }
            if(p>2)//x上移
            {
                swap(maze[p],maze[p-3]);
                int next=get_id();
                if(next!=init_id&&path[C][next]==-1)
                {
                    path[C][next]=now;
                    ans[C][next]='u';
                    que.push(next);
                }
                swap(maze[p],maze[p-3]);
            }
        }
    }
}

int main()
{
    init();
    cin.sync_with_stdio(false);//取消流同步
    int T_T;
    cin>>T_T;
    for(int cas=1;cas<=T_T;++cas)
    {
        res.clear();
        cin>>s1>>s2;
        int cnt=1,c=-1;
        for(int i=0;i<s1.length();++i)
            if(s1[i]=='X')
            {
                c=i;
                maze[i]=0;
            }
            else
            {
                f[s1[i]-'0']=cnt;
                maze[i]=cnt++;
            }
        for(int i=0;i<s2.length();++i)
            if(s2[i]=='X')
                maze[i]=0;
            else maze[i]=f[s2[i]-'0'];
        int id=get_id();
        while(path[c][id]!=-1)
        {
            res+=ans[c][id];
            id=path[c][id];
        }
        cout<<"Case "<<cas<<": "<<res.size()<<'\n';
        for(int i=res.size()-1;i>=0;--i)
            cout<<res[i];
        cout<<'\n';
    }
    
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值