HDU 3567 Eight II 八数码(2)(康拓展开判重+枚举)

题目链接https://vjudge.net/contest/372515#problem/A
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

翻译
从A状态到达B状态确保一定有解。
需要找到一个步数最少的解,然后还是要字典序最小。

康托展开(全排列到自然数的映射)

计算当前排列在所有由小到大全排列中的名次
在这里插入图片描述
cantor = sum[0]*(n-1)! + sum[1] * (n-2)! + … + sum[n-1] * 0 !

sum[i] 指的是位于位置i后面的小于a[i]的数的个数,后面乘的就是后面还有多少个数的阶乘

eg:
(1,2,3,4,5)5个数的排列组合中,计算 34152的康托展开值

cantor = 2 * 4! + 2 * 3! + 0 * 2! + 1 * 1! + 0 * 0!

bool state[362880];       //N的阶乘
int cantor(int x[])  //康拓展开进行判重
{
    int fac[]= {1,1,2,6,24,120,720,5040,40320};
    int t,sum=0;
    for(int i=0; i<9; i++)
    {
        t=0;
        for(j=i+1; j<9; j++)
        {
            if(x[j]<x[i])///求i后面的数比x[i]小的个数
                t++;
        }
        sum+=t*fac[8-i];///康拓值
    }
}

1. 枚举最基本的9种状态

char eight_num[9][11]= {"X12345678","1X2345678","12X345678","123X45678","1234X5678",
"12345X678","123456X78","1234567X8","12345678X"};///所有的状态都是由这9种状态得来的

所有可能的状态都可以看做是从这9种状态转移而来的

2. 映射

起始状态的数字和对应在九宫格中的位置一一映射。并根据X的位置判断是哪一种基本状态
同时
映射目标状态求出对应的康拓值

3.记录路径

int vis[10][N],pre[10][N];///vis[i][cantor]:state可以从基本状态i得来,同时记录cantor值

vis记录从上一个状态到下一个状态走的是哪一步,记录的是标号
pre记录当前状态是由哪一个状态转移而来的

4.初始化9种基本状态

每一种基本状态出发枚举所有能到达的状态,同时记录路径
区别不同状态的依据为:康拓值的不同

代码:

#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<stdlib.h>
#include<math.h>
#include<map>
#include<algorithm>
using namespace std;
const int N=4*1e5;
int n,m;
char eight_num[9][11]= {"X12345678","1X2345678","12X345678","123X45678","1234X5678","12345X678","123456X78","1234567X8","12345678X"};///所有的状态都是由这9种状态得来的
char step[4]= {'d','l','r','u'};
int to[4][2]= {1,0,0,-1,0,1,-1,0};
int fac[10]= {1,1,2,6,24,120,720,5040,40320};
int vis[10][N],pre[10][N];///vis[i][cantor]:state可以从基本状态i得来,同时记录cantor值
struct node
{
    int num[10];///九数码
    int state;///康拓值
    int pos;///X在一维数组中的下标
};
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[i]>s[j])
                num++;
        }
        sum+=(num*fac[8-i]);
    }
    return sum;
}
void bfs(node x,int kind)
{
    queue<node>q;
    q.push(x);
    node nex,u;
    vis[kind][x.state]=1;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        int x=u.pos/3;///将一维数组换算为九宫格对应的X在九宫格中的下标
        int y=u.pos%3;
        for(int i=0; i<4; i++)
        {
            int tx=x+to[i][0];
            int ty=y+to[i][1];
            if(tx<0||tx>=3||ty<0||ty>=3)
                continue;
            nex=u;///将u赋给nex,需要更改结构体中所有的变量
            swap(nex.num[u.pos],nex.num[tx*3+ty]);///将九宫格中的X坐标再次换算为一维数组中的下标
            nex.state=get_state(nex.num);
            if(vis[kind][nex.state]==-1)
            {
                nex.pos=tx*3+ty;
                vis[kind][nex.state]=i;///从u.state到v.state需要走的是step[i]
                pre[kind][nex.state]=u.state;
                q.push(nex);
            }
        }
    }
}
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);
}
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]);
    printf("\n");
}
int main()
{
    int t,flag=1;
    char s[15];
    int c[15],temp[15];
    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;
        for(int i=0; i<9; i++)
        {
            ///把起始状态映射到9种基本状态中的一种,kind记录
            ///12X453786
            ///1  12  2,X  X,4  35  43  57  68  76  8一一映射
            if(s[i]=='X')
            {
                c[0]=0;
                kind=i;
            }
            else
                c[s[i]-'0']=cnt++;
        }
        scanf("%s",s);
        for(int i=0; i<9; i++)
        {
            if(s[i]=='X')
                temp[i]=0;
            else
                temp[i]=c[s[i]-'0'];
        }
        int aim=get_state(temp);
        printf("Case %d: ",flag++);
        print(aim,kind);///从kind这种基本状态出发到达aim
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值