poj1198

poj1198:

Solitaire

Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right respectively.

There are four identical pieces on the board. In one move it is allowed to:

  • move a piece to an empty neighboring field (up, down, left or right),
  • jump over one neighboring piece to an empty field (up, down, left or right).

There are 4 moves allowed for each piece in the configuration shown above. As an example let's consider a piece placed in the row 4, column 4. It can be moved one row up, two rows down, one column left or two columns right.
Write a program that:

  • reads two chessboard configurations from the standard input,
  • verifies whether the second one is reachable from the first one in at most 8 moves,
  • writes the result to the standard output.

用广度优先搜索求最小路径(其实原题给定了最大步数,深搜搜到第八步跳出循环也可以解决,但是容易超时)。

根据广度优先搜索的几个基本步骤,在写题之前先把思路整理好:

  1. 每个结点的棋盘都将只有四个点是棋子,也就是说,与其用8x8 的数组存储,不如只存坐标。
  2. 每个父结点的每个棋子都有4种走法,四个棋子有16种走法。如果搜到8步,那将是 16^8,也就是 2^32 = 429496729,数组开不下,并且必定会超时。

解决的办法有两个:

  1. 判重并且使用到哈希解码,对于冲突,我们可以用字符串记录路径,发生冲突时叠加字符串即可。对于判重,从头到尾的比较肯定会超时,我们可以开一个86878889长度的数组(可能出现的最大项,可以用平移优化),用来打标记判重。
  2. 我们可以写一个双向BFS(个人更喜欢这种做法),双向搜索只需要搜4层就够了。

值得注意的是,如果开数组打标记的方法判重,一定要把每个结点的四个坐标都排好序!

以下是代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

//定义棋子
struct point{
    int x , y;
};
//定义初始、目标棋盘
point a[5] , b[5];
int numa , numb;
//输出棋盘
void print( point a[5] ){
    for( int i = 1; i <= 4; ++i ) printf( "%d%d" , a[i].x , a[i].y );
    printf( "\n" );
}

//将棋盘转换为十进制数
int f( point a[5] ){
    int s = 0;
    for( int i = 1; i <= 4; ++i ) s = s * 100 + a[i].x * 10 + a[i].y;
    return s;
}
bool cmp( point a , point b ){
    return a.x < b.x or ( a.x == b.x and a.y < b.y );
}

//偏移量
point d[5] = {{ 0 , 0 } ,
              { +1 , 0 } ,
              { -1 , 0 } ,
              { 0 , +1 } ,
              { 0 , -1 } };

//定义判重的数组 flag
bool flag[85868789];
//定义节点
struct node{
    point a[5];
    int dep;
    int fa;
};
node data[6000000];
//判断x,y位置是否出界
bool check( int x , int y ){
    return ( x >= 1 and x <= 8 and y >= 1 and y <= 8 );
}
//检查a棋盘的x,y位置是否有棋子 有返回真
bool check1( point a[5] , int x , int y ){
    for( int i = 1; i <= 4; ++i )
        if( a[i].x == x and a[i].y == y ) return true;
    return false;
}
//复制棋盘
void copy1( point a[5] , point b[5] ){
    for( int i = 1; i <= 4; ++i ) b[i].x = a[i].x , b[i].y = a[i].y;
}
int op , cl;
void print1( int cl ){
    int ans[10] , n = 0;
    while( cl != -1 ){
        ans[++n] = cl;
        cl = data[cl].fa;
    }
    printf( "\n\n" );
    for( int i = n; i > 1; --i ) print( data[ans[i]].a );
    print( data[ans[1]].a );
}
int main(){
    //freopen("poj1198.in", "r", stdin);
    for( int i = 1; i <= 4; ++i ) scanf( "%d%d" , &a[i].x , &a[i].y );
    for( int i = 1; i <= 4; ++i ) scanf( "%d%d" , &b[i].x , &b[i].y );
    //print(a);
    //print(b);
    sort( a + 1 , a + 5 , cmp );
    sort( b + 1 , b + 5 , cmp );
    numa = f( a );
    numb = f( b );
    //print(a);
    //print(b);
    //printf("%d %d\n", numa, numb);
    //特判初始状态和目标状态
    if( numa == numb ){
        printf( "YES" );
        return 0;
    }
    //初始化 首节点入队 首尾设置指针
    copy1( a , data[1].a );
    data[1].dep = 0;
    data[1].fa = -1;
    //memset(flag, false, sizeof flag);
    flag[numa] = true;

    op = 0 , cl = 1;
    bool f1 = false;
    while( op < cl and !f1 and data[op + 1].dep < 8 ){
        ++op;

        int dep = data[op].dep;

        for( int i = 1; i <= 4; ++i ){
            for( int j = 1; j <= 4; ++j ){
                copy1( data[op].a , a ); //每个操作要从原始棋盘开始

                //x1,y1移动一步的位置
                //x2,y2跳跃一步的位置
                int x1 = a[i].x + d[j].x , y1 = a[i].y + d[j].y;
                int x2 = x1 + d[j].x , y2 = y1 + d[j].y;

                if( check( x1 , y1 ) and !check1( a , x1 , y1 ) ){ //x1,y1位置不出界且x1,y1位置无棋子

                    a[i].x = x1;
                    a[i].y = y1;               //移动到新位置
                    sort( a + 1 , a + 5 , cmp ); //排序
                    numa = f( a );             //转化为数
                    if( !flag[numa] ){        //判重
                        flag[numa] = true;     //标记

                        ++cl; //入队
                        copy1( a , data[cl].a );
                        data[cl].dep = dep + 1;
                        data[cl].fa = op;

                        //判断目标
                        if( numa == numb ){
                            f1 = true;
                            break;
                        }
                    }
                } else if( check( x1 , y1 ) and check( x2 , y2 ) and
                           check1( a , x1 , y1 ) and !check1( a , x2 , y2 ) ){
                    //x1,y1,x2,y2位置不出界且x1,y1位置有棋子,x2,y2位置无棋子

                    a[i].x = x2;
                    a[i].y = y2;               //跳跃到新位置
                    sort( a + 1 , a + 5 , cmp ); //排序
                    numa = f( a );             //转化为数
                    if( !flag[numa] ){        //判重
                        flag[numa] = true;     //标记

                        ++cl; //入队
                        copy1( a , data[cl].a );
                        data[cl].dep = dep + 1;
                        data[cl].fa = op;

                        //判断目标
                        if( numa == numb ){
                            f1 = true;
                            break;
                        }
                    }
                }
            }
            if( f1 ) break;
        }
    }
    if( f1 ) printf( "YES\n" );
    else
        printf( "NO\n" );
    return 0;
}
posted @ 2019-05-25 15:41  hélium  阅读(173)  评论(0)    收藏  举报