UVA 1343 The Rotation Game

本文介绍了一种基于八数码问题的启发式搜索方法,通过减少状态空间并利用IDA*算法来解决一个涉及24个数排列的问题。文章详细阐述了状态表示、剪枝条件以及搜索策略。

首先我刚看到题目的时候,便想到了八数码,同样是基于状态空间的搜索。但是我们保存每个状态的却遇到了困难,如果要保存这24个数的排列的情况,则状态空间便成为24!/(8!*8!*8!),状态空间非常巨大。现给出一种减少状态空间的方法,我们可每次只考虑中间方格内的数为1,2,3的三种情况,则对于一种情况我们,其余另外两个数我们便于同等对待,这样我们的状态空间便成为了C(24,8),大小是我们能够承受的。我们保存状态的方法,按照当前数的位置从小到大排序,每个位置占两位,我们便可以将当前的状态表示为一个16位的十进制数。我们判重的时候可以用set、map、hash等。这样进行三次bfs便可以求解。

其实还有一种更好的方法,便是利用IDA*算法,我们可以设置一个搜索的深度maxd,从小到大依次枚举maxd,求解出最小的maxd。我们利用这个maxd便可以设计估价函数,我们给出的估价函数是当前状态下中间方格中最少改变多少个数可以使所有的数都相同,设其为f,则剪枝的条件便为当前深度cur+f>maxd,这样我们dfs的时候利用这个条件进行剪枝处理。到这里我只能说IDA*太好用了,还得做几道IDA*的题目来加深理解。另外本题需要一点的处理技巧,具体看代码。


#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 110;
const int inf  = 0x3f3f3f3f;

int arr[maxn];
int pos[8][7] = { \
                { 0, 2, 6, 11, 15, 20, 22 }, \
                { 1, 3, 8, 12, 17, 21, 23 }, \
                { 10, 9, 8, 7, 6, 5, 4 }, \
                { 19, 18, 17, 16, 15, 14, 13 }, \
                { 23, 21, 17, 12, 8, 3, 1 }, \
                { 22, 20, 15, 11, 6, 2, 0 }, \
                { 13, 14, 15, 16, 17, 18, 19 }, \
                { 4, 5, 6, 7, 8, 9, 10 } \
                };
int center[8] = { 6, 7, 8, 11, 12, 15, 16, 17 };

int calculate(int x) {
    int cnt = 0;
    for(int i = 0; i < 8; ++i) {
        if(arr[center[i]] != x) {
            cnt++;
        }
    }
    return cnt;
}

char op[1010];

bool check() {
    for(int i = 0; i < 8; ++i) {
        if(arr[center[i]] != arr[center[0]]) {
            return false;
        }
    }
    return true;
}

void change_one(int x) {
    int t = arr[pos[x][0]];
    for(int i = 0; i < 6; ++i) {
        arr[pos[x][i]] = arr[pos[x][i+1]];
    }
    arr[pos[x][6]] = t;
    return ;
}

void change_two(int x) {
    int t = arr[pos[x][6]];
    for(int i = 6; i > 0; --i) {
        arr[pos[x][i]] = arr[pos[x][i-1]];
    }
    arr[pos[x][0]] = t;
    return ;
}

int get_min_step() {
    int cnt = inf;
    for(int i = 1; i <= 3; ++i) {
        cnt = min(cnt, calculate(i));
    }
    return cnt;
}

bool dfs(int cur, int maxd) {
    if(cur == maxd) {
        if(check()) {
            return true;
        }
        return false;
    }
    if(cur + get_min_step() > maxd) return false;
    for(int i = 0; i < 8; ++i) {
        op[cur] = 'A' + i;
        change_one(i);
        if(dfs(cur + 1, maxd)) {
            return true;
        }
        change_two(i);
    }
    return false;
}

int main() {

    //freopen("aa.in", "r", stdin);

    int maxd;
    while(true) {
        for(int i = 0; i < 24; ++i) {
            scanf("%d", &arr[i]);
            if(arr[i] == 0) return 0;
        }
        if(check()) {
            printf("No moves needed\n");
            printf("%d\n", arr[6]);
        } else {
            for(maxd = 1; ; ++maxd) {
                memset(op, 0, sizeof(op));
                if(dfs(0, maxd)) {
                    printf("%s\n", op);
                    printf("%d\n", arr[6]);
                    break;
                }
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值