poj 1077 Eight

7 篇文章 0 订阅
2 篇文章 0 订阅

 

Eight
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 9915 Accepted: 4303 Special Judge

Description

The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as:
 1  2  3  4 

 5  6  7  8 

 9 10 11 12 

13 14 15  x 

where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
 1  2  3  4    1  2  3  4    1  2  3  4    1  2  3  4 

 5  6  7  8    5  6  7  8    5  6  7  8    5  6  7  8 

 9  x 10 12    9 10  x 12    9 10 11 12    9 10 11 12 

13 14 11 15   13 14 11 15   13 14  x 15   13 14 15  x 

           r->           d->           r-> 

The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively.

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course).

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.

Input

You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle
 1  2  3 

 x  4  6 

 7  5  8 

is described by this list:
 1 2 3 x 4 6 7 5 8 

Output

You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.

Sample Input

 2  3  4  1  5  x  7  6  8 

Sample Output

ullddrurdllurdruldr

Source


/*好久没有遇到这么大快人心的题了,虽然做了一天多,但是这次一改以往遇到陌生题比较浮躁的态度,认真去学习了相关知识。
通过这道题学习了A*算法,以及排列数一种常见的hash算法,并实际使用和操作了最大堆,可以说是收获颇多
*/
/*注意程序中的两个优化:
如果两个优化都没有,则会TLE
如果只用优化2,而不用优化1,所用时间为,同样会TLE
如果只用优化1,而不用优化2,所用时间为:
5782538	bobten2008	1077	Accepted	8752K	47MS	C++	7345B	2009-08-30 14:52:15
如果两个都用,则时间为:
5782475	bobten2008	1077	Accepted	9128K	16MS	C++	7329B	2009-08-30 14:41:28
所以可见优化二的作用是决定性的
*/
#include <iostream>
#define MAX_N 362880 //利用阶乘逆序数来计算Hash值,最大hash值是9! - 1
using namespace std;
//0-9的阶乘,用来计算哈希值
int fac[10] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int temp[10];
//记录1 - 9 九个位置可以移动的方向的个数
int dirN[10] = {0, 2, 3, 2, 3, 4, 3, 2, 3, 2};
//记录1 - 9 九个位置可以移动的方向的种类
char dirT[10][6] =  {
{' '},
{' ', 'r', 'd'},
{' ', 'l', 'r', 'd'},
{' ', 'l', 'd'},
{' ', 'u', 'r', 'd'},
{' ', 'u', 'l', 'r', 'd'},
{' ', 'u', 'l', 'd'},
{' ', 'u', 'r'},
{' ', 'u', 'l', 'r'},
{' ', 'u', 'l'}};
int heap[MAX_N + 5];
int heapSize = 0;
int initState, finalState = 123456789, curPos;
struct node
{
    int type;   //0 尚未访问, 1 已经在待考察队列中 2 被访问过并从待访问队列中删除了
	int heapPos; //当前状态在堆中的位置
    int state;  //当前状态,如123456789
    char dType; //上一个状态到当前状态所走的方向
    int diff;   //A*算法中的h()用当前状态和终止状态之间的different * 10 表示(不计空格)
    int step;   //A*算法中的g(),表示当前遍历的深度
    int fVal;   //diff和step的和,堆算法主要基于这个指标
    node()
    {
        step = 0;
        type = 0;
    }
}hashT[MAX_N + 5]; //哈希表
int eP(int l)  //计算10的l次方
{
    int res = 1;
    for(int i = 1; i <= l; i++)
        res *= 10;
    return res;
}
int getNum(int which, int num) //通过当前状态取3 × 3方格中的第which个数
{
    if(which > num % 10)
        return num / eP(9 - which + 1) % 10;
    else
        return num / eP(9 - which) % 10;
}
//以下均是移动空格的位置
int moveL(int state) //由当前状态左移得到新的状态
{
    return state - 1;
}
int moveR(int state) //由当前状态右移得到新的状态
{
    return state + 1;
}
int moveU(int state) //由当前状态下移得到新的状态
{
    int t0 = 9 - state % 10 + 1;
    int t1 = state / eP(t0);
    int t2 = t1 % 1000;
    t1 = t1 - t2 + (t2 % 100) * 10 + t2 / 100;
    t1 = t1 * eP(t0);
    return t1 + state % eP(t0) - 3;
}
int moveD(int state) //由当前状态上移得到新的状态
{
    int t0 = 7 - state % 10;
    int t1 = state / eP(t0);
    int t2 = t1 % 1000;
    t1 = t1 - t2 + (t2 % 10) * 100 + t2 / 10;
    t1 = t1 * eP(t0);
    return t1 + state % eP(t0) + 3;
}
int getRevNum(int state) //得到当前状态的逆序数,最后一位表示空格,不计
{
    int blank = state % 10;
    int total = 0;
    int p = 0;
    for(int id = 1; id <= 9; id++)
    {
        if(id == blank)
            continue;
        int n = getNum(id, state);
        temp[p++] = n;
        for(int k = p - 2; k >= 0; k--)
            if(temp[k] >= n)
                total++;
    }
    return total;
}
//通过当前状态得到其对应的哈希值
//这里计算哈希值采用1-9每位的逆序数乘以(位数减一的阶乘)并取和的方法,这样
//可以保证最大的哈希值为9! - 1且没个状态的哈希值都唯一
int getHashNum(int state) 
{
    int blank = state % 10;
    int hashVal = 0;
    int p = 0, total;
    int pp = 0; //pp记录阶乘的位置
    for(int id = 1; id <= 9; id++)
    {
        pp++;
        if(id == blank)
            continue;
        int n = getNum(id, state);
        temp[p++] = n;
        total = 0;
        for(int k = p - 2; k >= 0; k--)
        {
            if(temp[k] >= n)
                total++;
        }
        hashVal += total * fac[p - 1];
    }
    // 空格作为最小值计算
    return hashVal + (blank - 1) * fac[blank - 1];

}
//计算两个状态的距离
int getDiff(int state1, int state2)
{
    int blank1 = state1 % 10, blank2 = state2 % 10, i, diff = 0;
	if(blank1 != blank2) diff += 2;
    for(i = 1; i <= 9; i++)
    {
        if(i != blank1 && i != blank2 && getNum(i, state1) != getNum(i, state2))
			diff++;
    }
    return diff;
}
void swapHeapV(int i, int j)
{
    int temp = heap[i];
    heap[i] = heap[j];
    heap[j] = temp;
	hashT[heap[i]].heapPos = i;
	hashT[heap[j]].heapPos = j;
}
//从i结点保证堆的性质
void maxHeapify(int i)
{
    int l = 2 * i, r = 2 * i + 1, largest;
    if(l <= heapSize && hashT[heap[l]].fVal < hashT[heap[i]].fVal)
        largest = l;
    else
        largest = i;
    if(r <= heapSize && hashT[heap[r]].fVal < hashT[heap[largest]].fVal)
        largest = r;
    
    if(largest != i)
    {
        swapHeapV(i, largest);
        maxHeapify(largest);
    }
}
//取堆中的最大值
int getHeapMax()
{
    return heap[1];    
}
//取堆中的最大值,并删除这个值,需要用maxHeapify(1)来保持最大堆的性质
int extracMaxHeap()
{
    if(heapSize < 1)
        return -1;
    int maxv = heap[1];
    heap[1] = heap[heapSize];
	hashT[heap[1]].heapPos = 1;
    heapSize--;
    maxHeapify(1);
    return maxv;
}
//向对中插入结点i
void heapInsert(int i)
{
    heapSize++;
    heap[heapSize] = i;
	hashT[i].heapPos = heapSize;
    int pos = heapSize;
    while(pos > 1 && hashT[heap[int(pos / 2)]].fVal > hashT[heap[pos]].fVal)
    {
        swapHeapV(pos, int(pos / 2));
        pos = pos / 2;
    }
}
//当结点i的权值发生变化时,需要更新堆的结构以保证最大堆的性质
void upMoveHeap(int i)
{
    int pos = i;
    while(pos > 1 && hashT[heap[int(pos / 2)]].fVal > hashT[heap[pos]].fVal)
    {
        swapHeapV(pos, int(pos / 2));
        pos = pos / 2;
    }
}
//递归打印结果
void printRes(int curState)
{
    int hashVal = getHashNum(curState);
    char dType = hashT[hashVal].dType;
    if(dType != 's')
    {
        
        if(dType == 'l')
        {
            printRes(moveR(curState));
            cout<<'l';
        }
        else if(dType == 'r')
        {
            printRes(moveL(curState));
            cout<<'r';
        }
        else if(dType == 'u')
        {
            printRes(moveD(curState));
            cout<<'u';
        }
        else if(dType == 'd')
        {
            printRes(moveU(curState));
            cout<<'d';
        }
    }
}
//计算的核心控制函数
void getBest()
{
    //计算并初始化初始结点的相关性质
    int h = getHashNum(initState), d, curHashVal, curState, xPos, nextState, nextHashVal, nextHeapPos;
    char type;
    hashT[h].state = initState;
    hashT[h].type = 1;
    hashT[h].dType = 's';
    hashT[h].step = 0;
	//将初始节点插入堆中
    heapInsert(h);
    while(true)
    {
        //取当前堆中最大的结点
        curHashVal = extracMaxHeap();
        //设置当前状态为从待考察队列中删除
        hashT[curHashVal].type = 2;
        
        curState = hashT[curHashVal].state;
        
        //到达终止结点
        if(curState == finalState)
        {
            printRes(curState);
            cout<<endl;
            break;
        }
        xPos = curState % 10;
        
        //遍历空格可以走的方向
        for(d = 1; d <= dirN[xPos]; d++)
        {
            type = dirT[xPos][d];
            if(type == 'r')
            {
                //从当前节点向右走到达新的状态
                nextState = moveR(curState);
                //计算新状态的哈希值
                nextHashVal = getHashNum(nextState);
				nextHeapPos = hashT[nextHashVal].heapPos;
                //如果此状态已经被访问过
                if(hashT[nextHashVal].type != 0)
                {
                    //优化1:更新此状态的g函数,即遍历的深度
                    if(hashT[curHashVal].step + 1 < hashT[nextHashVal].step)
                    {
                        hashT[nextHashVal].step = hashT[curHashVal].step + 1;
                        hashT[nextHashVal].fVal = hashT[nextHashVal].step + hashT[nextHashVal].diff;
                        //更新堆的结构以保证最大堆的性质
                        upMoveHeap(nextHeapPos);
                    }
                    continue;
                }
                //记录方向
                hashT[nextHashVal].dType = 'r';
            }
            else if(type == 'l')
            {
                nextState = moveL(curState);
                nextHashVal = getHashNum(nextState);
				nextHeapPos = hashT[nextHashVal].heapPos;
                if(hashT[nextHashVal].type != 0)
                {
                    if(hashT[curHashVal].step + 1 < hashT[nextHashVal].step)
                    {
                        hashT[nextHashVal].step = hashT[curHashVal].step + 1;
                        hashT[nextHashVal].fVal = hashT[nextHashVal].step + hashT[nextHashVal].diff;
                        upMoveHeap(nextHeapPos);
                    }
                    continue;
                }
                hashT[nextHashVal].dType = 'l';
            }
            else if(type == 'u')
            {
                nextState = moveU(curState);
                nextHashVal = getHashNum(nextState);
				nextHeapPos = hashT[nextHashVal].heapPos;
                if(hashT[nextHashVal].type != 0)
                {
                    if(hashT[curHashVal].step + 1 < hashT[nextHashVal].step)
                    {
                        hashT[nextHashVal].step = hashT[curHashVal].step + 1;
                        hashT[nextHashVal].fVal = hashT[nextHashVal].step + hashT[nextHashVal].diff;
                        upMoveHeap(nextHeapPos);
                    }
                    continue;
                }
                hashT[nextHashVal].dType = 'u';
            }
            else if(type == 'd')
            {
                nextState = moveD(curState);
                nextHashVal = getHashNum(nextState);
				nextHeapPos = hashT[nextHashVal].heapPos;
                if(hashT[nextHashVal].type != 0)
                {
                    if(hashT[curHashVal].step + 1 < hashT[nextHashVal].step)
                    {
                        hashT[nextHashVal].step = hashT[curHashVal].step + 1;
                        hashT[nextHashVal].fVal = hashT[nextHashVal].step + hashT[nextHashVal].diff;
                        upMoveHeap(nextHeapPos);
                    }
                    continue;
                }
                hashT[nextHashVal].dType = 'd';
            }
            
            //存储并计算新状态的相关信息
            hashT[nextHashVal].state = nextState;
            //g函数:遍历的深度加一
            hashT[nextHashVal].step = hashT[curHashVal].step + 1;
            //h函数:优化2,将h函数乘以10可以降低深度的影响,从而提高A*算法的效率,但是结果一般不是最优的了
            hashT[nextHashVal].diff = getDiff(nextState, finalState) * 10;
            //计算:f函数即g函数和h函数之和
            hashT[nextHashVal].fVal = hashT[nextHashVal].diff + hashT[nextHashVal].step;
            //标记此状态已经加入待考察队列
            hashT[nextHashVal].type = 1;
            //将新状态加入堆(待考察队列)
            heapInsert(nextHashVal);
        }
    }
}
int main()
{
    
    char ch;
    int i;
    initState = 0;
    for(i = 1; i <= 9; i++)
    {
        cin>>ch;
        if(ch == 'x')
            curPos = i;
        else
            initState = initState * 10 + int(ch - '0');
    }
    initState = initState * 10 + curPos;
    int revInit = getRevNum(initState);
    if(revInit % 2 != 0)
        cout<<"unsolvable"<<endl;
    else if(initState == finalState)
        cout<<"lr"<<endl;
    else
        getBest();
 
 return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值