[IDA*] ZOJ 2477

最大深度只有5层, 所以用IDA* 算法

每块魔方的中间位置的颜色是不会变的,因此可以用总共不在原来面上的方块数来作为h函数


模拟每一个旋转变换有些难看,拿笔自己写一写就知道了


#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int n, m;
int depth;

int ans[ 10 ], dir[ 10 ];
char a[ 100 ];

//所有的数字都代表位置
int centre[ 6 ] = {5, 23, 26, 29, 32, 50};
int row[ 6 ][ 10 ] = {{1, 2, 3, 4, 5, 6, 7, 8, 9},          {10, 11, 12, 22, 23, 24, 34, 35, 36},
                      {13, 14, 15, 25, 26, 27, 37, 38, 39}, {16, 17, 18, 28, 29, 30, 40, 41, 42},
                      {19, 20, 21, 31, 32, 33, 43, 44, 45}, {46, 47, 48, 49, 50, 51, 52, 53, 54}};

/*
给每个位置标号

          1  2  3
          4  5  6
          7  8  9
 10 11 12 13 14 15 16 17 18 19 20 21
 22 23 24 25 26 27 28 29 30 31 32 33
 34 35 36 37 38 39 40 41 42 43 44 45
          46 47 48
          49 50 51
          52 53 54
 */

/*
 * 单独第0个面
 *       1  4  7
 *     ----------
 * 21 | 10 11 12 | 13
 * 33 | 22 23 24 | 25
 * 45 | 34 35 36 | 37
 *     ----------
        52 49 46

*/

// 6个面,每个面顺逆时针
//每两行一组,如change[0] --> change[1] 就是 12 移到 10,逆时针
int change[ 12 ][ 20 ] = {
    {12, 24, 36, 35, 34, 22, 10, 11, 13, 25, 37, 46, 49, 52, 45, 33, 21, 1, 4, 7},
    {10, 11, 12, 24, 36, 35, 34, 22, 1, 4, 7, 13, 25, 37, 46, 49, 52, 45, 33, 21},
    {15, 27, 39, 38, 37, 25, 13, 14, 16, 28, 40, 48, 47, 46, 36, 24, 12, 7, 8, 9},
    {13, 14, 15, 27, 39, 38, 37, 25, 7, 8, 9, 16, 28, 40, 48, 47, 46, 36, 24, 12},
    {18, 30, 42, 41, 40, 28, 16, 17, 19, 31, 43, 54, 51, 48, 39, 27, 15, 9, 6, 3},
    {16, 17, 18, 30, 42, 41, 40, 28, 9, 6, 3, 19, 31, 43, 54, 51, 48, 39, 27, 15},
    {21, 33, 45, 44, 43, 31, 19, 20, 3, 2, 1, 10, 22, 34, 52, 53, 54, 42, 30, 18},
    {19, 20, 21, 33, 45, 44, 43, 31, 42, 30, 18, 3, 2, 1, 10, 22, 34, 52, 53, 54},
    {3, 6, 9, 8, 7, 4, 1, 2, 18, 17, 16, 15, 14, 13, 12, 11, 10, 21, 20, 19},
    {1, 2, 3, 6, 9, 8, 7, 4, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10},
    {48, 51, 54, 53, 52, 49, 46, 47, 40, 41, 42, 43, 44, 45, 34, 35, 36, 37, 38, 39},
    {46, 47, 48, 51, 54, 53, 52, 49, 37, 38, 39, 40, 41, 42, 43, 44, 45, 34, 35, 36}};

//所有面的中心不变,比较每个面周围八个和中心有多少个不一样的,作为距离
int geth () {
        int sum = 0;
        for ( int i = 0; i < 6; ++i )
                for ( int j = 0; j < 9; ++j )
                        if ( a[ row[ i ][ j ] ] != a[ centre[ i ] ] )
                                ++sum;
        return sum;
}

bool ID_Astar ( int d ) {
        int h = geth ();

        if ( d + ceil ( h / 12.0 ) > depth )
                return false;

        if ( h == 0 && d == depth )
                return true;

        char maze[ 100 ];
        //搜索12种操作
        for ( int i = 0; i < 12; ++i ) {
                memcpy ( maze, a, sizeof ( a ) );

                for ( int j = 0; j < 20; ++j )
                        a[ change[ i ][ j ] ] = maze[ change[ i ^ 1 ][ j ] ];

                //记录方向
                ans[ d ] = i / 2;
                if ( !( i & 1 ) )
                        dir[ d ] = 1;
                else
                        dir[ d ] = -1;

                if ( ID_Astar ( d + 1 ) )
                        return true;

                memcpy ( a, maze, sizeof ( maze ) );
        }
        return false;
}

int main () {
        int T;
        scanf ( "%d", &T );
        while ( T-- ) {
                for ( int i = 1; i <= 54; ++i )
                        cin >> a[ i ];
                if ( !geth () ) {
                        printf ( "0\n" );
                        continue;
                }

                //固定深度
                for ( depth = 1; depth <= 5; depth++ ) {
                        if ( ID_Astar ( 0 ) )
                                break;
                }

                if ( depth == 6 )
                        printf ( "-1\n" );
                else {
                        printf ( "%d\n", depth );
                        for ( int i = 0; i < depth; ++i )
                                printf ( "%d %d\n", ans[ i ], dir[ i ] );
                }
        }
        return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值