sicily1151&魔板2

Description

题目和A题相同,在这里我们把数据范围扩大:N可能超过10

请仔细考虑各种情况。
Input

输入包括多个要求解的魔板,每个魔板用三行描述。

第一行步数N,表示最多容许的步数。

第二、第三行表示目标状态,按照魔板的形状,颜色用18的表示。

当N等于-1的时候,表示输入结束。
Output

对于每一个要求解的魔板,输出一行。

首先是一个整数M,表示你找到解答所需要的步数。接着若干个空格之后,从第一步开始按顺序给出M步操作(每一步是ABC),相邻两个操作之间没有任何空格。

注意:如果不能达到,则M输出-1即可。
Sample Input
4
5 8 7 6
4 1 2 3
3
8 7 6 5
1 2 3 4
-1
Sample Output
2  AB
1  A
评分:M超过N或者给出的操作不正确均不能得分。

原题大意

魔板可进行三种固定操作:A上下行互换 B循环右移 C中间四个顺时针旋转。通过这三种操作可以得到许多不同的结果。现给出一种目标态和最大步数,求解是否能在最大步数内通过ABC三种操作得到目标态,如果可以输出最短操作序列,如不可,输出-1.


这一题基本和简单魔板相同,但是直接拿A的代码过不了。因为我做A题的方法只是用一个固定的数组充当队列,当N值扩大了之后,由于数组容量的限制,不可能放得下这么多的中间态,更不可能求出结果了。


数据结构

队列

结构体

算法分析:

广度优先搜索

康拓展开

解题思路:

用广度优先搜索遍历整个三叉树,在遍历的过程中判断新出现的中间态是否已经在队列中。若没有则将中间态入队,直到找到目标态或者操作序列长度大于规定长度。

判断是否在队列中的方法是设定flag[8!],魔板一共有八个数字,所以魔板有8!种可能状态。通过康托展开来确定状态在全排列的位置,并判断flag的值。

为使队列可以容纳众多的中间态,每次将已经进行完一轮ABC操作的父节点推出队列,使下一个节点成为队列头部正在操作的父节点。

判断是否大于规定长度的方法是在每一个节点中保存其回溯过程的所有操作的序列,如果队列中正在操作的父节点不为目标态,但是其操作序列长度已经等于了规定长度,即表示不可能在规定长度中找到目标态了。


逐步求精算法描述:

变量解释:

struct moban: string op 从root到该节点的操作序列  int x int y记录魔板在该节点的状态

int max 规定的最大操作步数

queue<moban> q 魔板中间态队列

moban root 根节点

moban *fp 指向队列头部正在被操作的父节点

moban rp  经过操作即将要进队的子节点

int factor[PermSize] 记录1-8的阶乘结果

bool statue[40321] 所有魔板状态的flag

函数解释:

moban opa() 操作A

moban opb() 操作B

moban opc() 操作C

void search() 遍历三叉树和中间态进队

bool comp() 判断当前节点是不是目标态

int Cantor() 康托展开 计算当前魔板状态位置


/*
 * main.cpp
 *
 *  Created on: 2014年9月16日
 */
#include <iostream>
#include <string>
#include <queue>
#include <memory.h>
#include <stdlib.h>

using namespace std;

struct moban {
    string op;
    int x;
    int y;
};
//操作A
moban opa(moban * fp) {
    moban rp;
    rp.x = fp->y;
    rp.y = fp->x;
    rp.op = fp->op + "A";
    return rp;
}
//操作B
 moban opb(moban * fp) {
    moban rp;
    rp.x = fp->x % 10 * 1000 + fp->x / 10;
    rp.y = fp->y % 10 * 1000 + fp->y / 10;
    rp.op = fp->op + "B";
    return rp;
}
//操作C
 moban opc(moban * fp) {
    moban rp;
    int i = (fp->x / 1000) * 1000;
    int j = fp->x - i;
    int a = j / 100;
    int b = (j - a * 100) / 10;
    int i1 = (fp->y / 1000) * 1000;
    int j1 = fp->y - i1;
    int c = j1 / 100;
    int d = (j1 - c * 100) / 10;
    rp.x = i + c * 100 + a * 10 + (fp->x % 10);
    rp.y = i1 + d * 100 + b * 10 + (fp->y % 10);
    rp.op = fp->op + "C";
    return rp;
}
//判断是否为结果
bool comp(int tx, int ty, moban cur) {
    if (cur.x == tx && cur.y == ty)
        return true;
    return false;
}
//康托展开求解
const int PermSize = 8;
int factory[PermSize] = { 0, 1, 2, 6, 24, 120, 720, 5040 };
int Cantor(int buf) {
    int i = 7, j, counted;
    long result = 0;
    int num[8];
    while (buf != 0) {
        num[i--] = buf % 10;
        buf /= 10;
    }

    for (i = 0; i < PermSize; i++) {
        counted = 0;
        for (j = i + 1; j < PermSize; ++j)
            if (num[i] > num[j])
                counted++;
        result = result + counted * factory[PermSize - i - 1];
    }
    return result;  //返回当前状态在全排列中的位置
}
//遍历三叉树和中间态进队
void search(int tx, int ty, int max, moban root) {
    bool statue[40321];
    queue<moban> q;
    moban *fp = new moban;

    memset(statue, false, sizeof(statue));
    //中间态没有出现过就将其入队并将statue设为true
    int inde = Cantor(root.x * 10000 + root.y);
    statue[inde] = true;
    q.push(root);

    while (!q.empty()) {
        *fp = q.front();
        q.pop();
        //如果超过了固定步数,输出-1返回
        if (fp->op.size() >= max) {
            cout << "-1" << endl;
            return;
        }

        moban rp;
        rp = opa(fp); //操作A
        if (comp(tx, ty, rp)) {  //判断是否为目标态
            cout << rp.op.size() << "  " << rp.op << endl;
            return;
        }
        //计算康托展开 判断是否应该入队
        inde = Cantor(rp.x * 10000 + rp.y);
        if (statue[inde] != true) {
            statue[inde] = true;
            q.push(rp);
        }
        rp = opb(fp);  //操作B
        if (comp(tx, ty, rp)) {  //判断是否为目标态
            cout << rp.op.size() << "  " << rp.op << endl;
            return;
        }
        //计算康托展开 判断是否应该入队
        inde = Cantor(rp.x * 10000 + rp.y);
        if (statue[inde] != true) {
            statue[inde] = true;
            q.push(rp);
        }
        rp = opc(fp); //操作C
        if (comp(tx, ty, rp)) {  //判断是否为目标态
            cout << rp.op.size() << "  " << rp.op << endl;
            return;
        }
        //计算康托展开 判断是否应该入队
        inde = Cantor(rp.x * 10000 + rp.y);
        if (statue[inde] != true) {
            statue[inde] = true;
            q.push(rp);
        }

    }
}

int main() {
    int max = 0;
    moban root;
    cin >> max;
    while (max != -1) {
        root.x = 1234;
        root.y = 8765;
        root.op = "";
        int tx = 0, ty = 0;
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        tx = a * 1000 + b * 100 + c * 10 + d;
        cin >> a >> b >> c >> d;
        ty = a * 1000 + b * 100 + c * 10 + d;
        if (tx == 1234 && ty == 8765)
            cout << "0" << endl;
        else
            search(tx, ty, max, root);
        cin >> max;
    }
    return 0;
}                                 

测试数据

Sample Input

4
4 1 2 3
8 5 6 7
2
1 2 3 4
5 6 7 8
3
8 7 6 5
1 2 3 4
0
1 3 7 4
5 2 6 8
-1

Sample Output

2 AB
1 A
0-1



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值