poj1184 聪明的打字员

来源:互联网 发布:ubuntu如何分区 编辑:程序博客网 时间:2024/04/30 17:14

注:本解题报告里的有些思路不是由本人想出,是通过百度搜出来的。所以在此膜拜一下做出来的大牛们。

(个人认为)这是BFS的进阶题,以前做过的BFS都是按照操作一步步来,最终找出目标状态。这题巧妙的地方在于可以无视一些操作和把操作分类。比如说:光标左移是可以无视的。因为光标移动是连续的,而且开始光标在最左边。也就是说,如果光标在第3个数的话,那么它一定经过0,1,2这几个位置。所以这时如果再考虑光标左移是没有意义的。另外就是操作分类。所谓操作分类就是把操作分为两类(这两类分别对状态做出不同的影响):一类移动光标,另一类就是直接把数加到目标状态,要注意的地方是,只有光标经过的地方才能进行加减。

这样一来,记录光标到过的位置就很重要了。一般能想到的是2^6种状态,但仔细考虑之后发现其实只有10种状态,分别是

0:0
1:0, 1
2:0, 1, 2
3:0, 1, 2, 3
4:0, 1, 2, 3, 4
5:0, 5
6:0, 1, 5
7:0, 1, 2, 5
8:0, 1, 2, 3, 5
9:0, 1, 2, 3, 4, 5

第6种状态之后会出现5这个位置,是因为swap1操作。

后记:

本题可以改进的地方有几个:

1.存储状态可以进一步压缩,据说只要10*6!就可以把所有状态存完

2.据说A*算法,但是我还没想到有效的评价函数(T_T)

3.看discuss,发现有人用双向BFS写出来了,看来我也要纠结一下如何用双向BFS写出来才行……

================传说中华丽的分割线=====================

 

 

#pragma warning(disable:4786)
#include <iostream>
#include <queue>
#include <cmath>
using namespace std;

#define N 1000000

int start, end, v[6];
bool s[N][10];

struct node
{
    int state;//当前状态
    int step;//移动步数
    int visited;//光标到达过的位置
/*
0:0
1:0, 1
2:0, 1, 2
3:0, 1, 2, 3
4:0, 1, 2, 3, 4
5:0, 5
6:0, 1, 5
7:0, 1, 2, 5
8:0, 1, 2, 3, 5
9:0, 1, 2, 3, 4, 5
*/

};

void toArray(int n, int *v)
{
    for (int i = 5; i >= 0; i--)
    {
        v[i] = n % 10;
        n /= 10;
    }
}

int toNum(int *v)
{
    int ans, i;

    for (i = 0, ans = 0; i < 6; i++)
        ans = ans * 10 + v[i];
   
    return ans;
}

//pos1:0 or 5, pos2:当前光标位置
int swapNum(int pos1, int pos2, int *v)
{
    int tmp, ans;

    //swap pos1 and pos2
    tmp = v[pos1];
    v[pos1] = v[pos2];
    v[pos2] = tmp;

    ans = toNum(v);

    //把数组恢复原样
    tmp = v[pos1];
    v[pos1] = v[pos2];
    v[pos2] = tmp;

    return ans;
}

int check(node& in)
{
    int tmp, res, i, a[6];

    toArray(in.state, a);
   
    tmp = in.visited;
    if (tmp <= 4)
    {
        for (i = tmp + 1; i < 6; i++)       
            if (a[i] != v[i]) return -1;

        for (res = 0, i = 0; i <= tmp; i++)
            res += abs(a[i] - v[i]);
    }
    else
    {
        tmp -= 5;
        for (i = tmp + 1; i < 5; i++)   
            if (a[i] != v[i]) return -1;

        for (res = 0, i = 0; i <= tmp; i++)
            res += abs(a[i] - v[i]);   
       
        res += abs(a[5] - v[5]);
    }

    return res;
}

int BFS()
{
    queue<node> q;
    node p;
    int ori, visited, tmp, a[6], res, minimum = N;
    memset(s, false, sizeof(s));

    p.state = start;
    p.step = 0;
    p.visited = 0;
    q.push(p);
    s[start][0] = true;

    while (!q.empty())
    {
        p = q.front();
        q.pop();

        ori = p.state;
        visited = p.visited;
        res = check(p);

        if (res != -1)
        {
            if (minimum > res + p.step) minimum = p.step + res;
        }

        p.step++;
        if (p.step >= minimum) continue;//剪枝

        toArray(p.state, a);
        //swap0
        if (p.visited > 0)
        {
            if (p.visited >= 5) tmp = swapNum(0, p.visited - 5, a);
            else tmp = swapNum(0, p.visited, a);

            if (!s[tmp][p.visited])
            {
                p.state = tmp;
                q.push(p);
                s[tmp][p.visited] = true;
            }
        }

        //swap1
        if (p.visited < 5)
        {
            tmp = swapNum(5, p.visited, a);
            p.visited += 5;           
        }
        else tmp = swapNum(5, p.visited - 5, a);

        if (!s[tmp][p.visited])
        {
            p.state = tmp;               
            q.push(p);
            s[tmp][p.visited] = true;
        }       

        //right
        p.state = ori;
        p.visited = visited;
        if (p.visited < 5)
        {           
            p.visited++;
            if (!s[p.state][p.visited]) q.push(p);
        }
        else if (p.visited < 9)
        {
            p.visited++;
            if (!s[p.state][p.visited]) q.push(p);
        }
    }

    return minimum;
}

int main()
{
    while (cin >> start >> end)
    {
        toArray(end, v);

        cout << BFS() << endl;
    }

    return 0;
}