【POJ1198 Solitaire 】 思路+解题报告+测试数据生成器

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
//#define DBG
//#define INPUT
//#define DBG1
using namespace std;

/**
        Problem: poj1198 - Solitaire
        Begin Time : 18:33 15th/Mar/2012
        End Time:   2:40 a.m. 16th/Mar/2012
        Test Data:
        见数据生成器代码
        Standard Output:
        用本程序对拼即可
        Thought:
          这个代码写的太垃圾了,比较崩溃。
          ①:状态压缩:
            棋盘上只有四个棋子,一行数字(x1 y1 x2 y2 x3 y3 x4 y4)
            代表了四个棋子在棋盘上的位置。
            我们把各个棋子按照x的大小进行排序。
            然后把其变成一个八位数来表示当前的棋盘状态
          ②:HASH排重 (*)
            把待搜索的棋盘状态插入自己的hash表中。
            注意,由于DFS是双向的,hash表要维护两个,一个是正向搜索的hash表
            一个是反向搜索的hash表。如果其的key发生冲突并且内容相同,那么插入失败。
          ③:状态搜索:
            每次从两个队列(一个表示从开始状态,一个表示从结束状态)搜索中取出表示该次搜索
            应该搜索的状态,然后将搜索的内容在不同的hash表里进行检索,例如:
            从begin_queue[front]取出的bval1(表示从开始状态搜索到的某个中间状态)
            应该在end_hash中检索,如果在end_hash中找到了该状态,并且该状态对应的
            end_deep(从结束状态到这个中间状态经过的步数)与当前状态的deep加起来小于等于8
            那么就代表找到了,标志isFound = true;然后输出
          ④:如何搜索
            搜索的时候还是四个向量,左右上下,只不过进行左右上下搜索的时候记得判断一下周围是否有棋子
            这个做法是把压缩过的状态还原成int tmp[8],然后找移动后的棋子tmp[i],tmp[i+1]在其他的tmp中有
            没有相同的,2*i是x,2*i+1是y
            如果tmp[2*i] == tmp[2*j] && tmp[2*i + 1] == tmp[2*j + 1]的话,就代表i移动后与j重叠。
            按照“跳过子”的规则进行移动。

        Knowledge:
          HASH排重+状态压缩+双向BFS搜索
        The Experience:
            WA点1 : 在进行棋子的移动的时候,switch(dir) case 0 1 2 3写成了 1 2 3 4,不应该犯!
            WA点2:非常脑残的在hash表中每个状态没有保存对应的深度!所以有时候从beginState搜索到beginDeep = 3
                    的时候,在endHash表中找到了一个endDeep = 6 的状态与之对应,由于没有保存endHash中每个状态的深度
                    所以这时候输出了,WA了4次(貌似)
*/
const int HASHKEY = 100003;
const int HASHEND = 2;
const int HASHBEGIN = 1;
const int dx[] = {-1,1,0,0};
const int dy[] = {0,0,-1,1};
int bs[8];  ///开始状态
int es[8];  ///结束状态
int bhash[200000];
int bnext[200000];
int ehash[200000];
int enext[200000];
int _bdeep1[500000];
int _edeep1[500000];
int _bdeep[500000];
int _edeep[500000];
int bque[500000];
int eque[500000];
bool isFound;
int bnow,enow;
int bfront,brear;
int bdeep,edeep;
int efront,erear;
bool checkHash(int val,int sel,int dp)
{
    int tmp = val % HASHKEY;
    if( sel == HASHBEGIN )
    {
        while(bnext[tmp])
        {
            if ( bhash[ bnext[tmp] ] == val )
            {
#ifdef DBG
                printf("The %d of bhash is %d\n",bnext[tmp],val);
#endif
                if ( _bdeep1[ bnext[tmp] ] + dp <= 8)
                    return true;
                else
                    return false;
            }
            tmp = bnext[tmp];
        }
    }
    if( sel == HASHEND )
    {
        while(enext[tmp])
        {
            if (ehash[ enext[tmp] ] == val)
            {
#ifdef DBG
                printf("The %d of ehash is %d\n",bnext[tmp],val);
#endif
                if (_edeep1 [ enext[tmp] ] + dp <= 8 )
                    return true;
                else
                    return false;
            }
            tmp = enext[tmp];
        }
    }
    return false;
}
bool insert_end(int es,int dp)
{
    int key = es % HASHKEY;
    while( enext[key] )
    {
        if( ehash[ enext[key] ] == es) return false;
        key = enext[key];
    }
    enext[key] = enow;
    _edeep1[enow] = dp;
    ehash[enow] = es;
    enow++;
    return true;
}
bool insert_begin(int bs,int dp)
{
    ///检查是否在hashend里
    int key = bs % HASHKEY;
    while( bnext[key] )
    {
        if ( bhash[ bnext[key] ] == bs ) return false;
        key = bnext[key];
    }
    bnext[key] = bnow;
    bhash[bnow] = bs;
    _bdeep1[bnow] = dp;
    bnow++;
    return true;
}
void exchange(int* a, int* b)
{
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}
int getNum(int a[8])
{
    for(int i = 0 ; i < 8 ; i = i + 2)
    {
        for (int j = i + 2 ; j < 8 ; j = j + 2)
        {
            if ( a[i] > a[j] )
            {
                exchange(&a[i],&a[j]);
                exchange(&a[i+1],&a[j+1]);
            }
            if ( a[i] == a[j] )
            {
                if ( a[i+1] > a[j+1] )
                {
                    exchange(&a[i+1],&a[j+1]);
                }
            }
        }
    }
    ///排序
    int res = 0 ;
    for(int i = 0 ; i < 8 ; i++)
    {
        res = res * 10 + a[i];
    }
    return res;
}
/**
int getNum(const int a[8])
{
    ///这里写的有问题
    ///应该把node按照x,y的顺序排序之后再给值!
    int res = 0;
    for(int i = 0 ; i < 8; i++)
    {
        res = res * 10 + a[i];
    }
    return res;
}*/
bool checkMove(int* destState,int oriState[8],int ind,int dir)
{
    for(int i = 0 ; i < 8; i = i + 2)
    {
        if( destState[ind] == oriState[i]  )
        {
            if (destState[ind+1] == oriState[i+1]  )
            {
                ///棋子发生重叠
                ///dir - >方向
                switch(dir)
                {
                case 0:  ///上,x-1,x - 1 -1 ->越过棋子
                    if( destState[ind] - 1 > 0 )  ///不出界
                    {
                        destState[ind] = destState[ind] - 1;
                        return true;
                    }
                    break;
                case 1:  ///下, x+1
                    if ( destState[ind] + 1 < 9 )
                    {
                        destState[ind] += 1;
                        return true;
                    }
                    break;
                case 2:  ///左,y-1
                    if ( destState[ind+1] - 1 > 0 )
                    {
                        destState[ind+1] -=1;
                        return true;
                    }
                    break;
                case 3:  ///右,y+1
                    if ( destState[ind+1] + 1 < 9 )
                    {
                        destState[ind+1] +=1;
                        return true;
                    }
                    break;
                }
                return false;
            }
        }
    }
    /////////////////////上方为检测棋子重叠
    if ( destState[ind] < 1 || destState[ind] > 8
            || destState[ind+1] > 8 || destState[ind+1] < 1 )
        return false;
    return true;
}
void segNum(int* a,int val)
{
    for(int i = 7 ; i >= 0  ; i--)
    {
        a[i] = val % 10;
        val = val / 10;
    }
}
void Solve()
{
    int btmp[8];
    int etmp[8];
    int btmp1[8],etmp1[8];
    int bval = 0;
    int bval1 = 0;
    int eval = 0;
    int bdeep1,edeep1;
    int eval1 = 0;
    int bnum,_enum; //enum is the keyword
    isFound = false;
    bfront = 1;
    brear = 2;
    bdeep = edeep = 0;
    efront = 1;
    erear = 2;
    bnow = enow = 1;
    memcpy(btmp,bs,sizeof(int)*8);
    memcpy(etmp,es,sizeof(int)*8);
    memset(bque,0,sizeof(bque));
    memset(eque,0,sizeof(eque));
    memset(bnext,0,sizeof(bnext));
    memset(enext,0,sizeof(enext));
    memset(bhash,0,sizeof(bhash));
    memset(ehash,0,sizeof(ehash));
    bnum = getNum(btmp);
    _enum = getNum(etmp);
    insert_begin(bnum,bdeep);
    insert_end(_enum,edeep);
    bque[bfront] = bnum;
    eque[efront] = _enum;
    _bdeep[bfront] = bdeep;
    _edeep[efront] = edeep;
    while( bfront < brear && efront < erear && !isFound )
    {
        ///双向搜索
        bval = bque[bfront];
        eval = eque[efront];
        bdeep = _bdeep[bfront];
        edeep = _edeep[efront];
//        if ( bdeep + edeep >= 6 )
        if(checkHash(bval,HASHEND,bdeep))
        {
            isFound = true;
        }
        if(checkHash(eval,HASHBEGIN,edeep))
        {
            isFound = true;
        }
#ifdef DBG
        if( isFound )
        {
            printf("Founded at deep of : \n");
            printf("Begin Deep : %d \n",bdeep);
            printf("End Deep : %d \n",edeep);
            printf("The end is %d\n",eval1);
            printf("The Start is %d\n",bval1);
        }
#endif
        if(isFound)
            return;
        if( bdeep + edeep > 8 )
            break;
        segNum(btmp,bval);
        segNum(etmp,eval);
        ///这里就比较郁闷了,对于每个棋子都要展开,
        ///对于每个棋子的移动都要判断是否符合规矩
        ///其实rear代表着队列里面的节点数,我们从节点少的开始展开。
        for(int i = 0 ; i < 4 ; i++)   ///四个棋子
        {
            //      memcpy(btmp1,btmp,sizeof(btmp));
            //      memcpy(etmp1,etmp,sizeof(etmp));

            for(int j = 0 ; j < 4 ; j++)  ///四个方向
            {
                memcpy(btmp1,btmp,sizeof(btmp));
                memcpy(etmp1,etmp,sizeof(etmp));
                if(isFound) break;
                btmp1[2*i] = btmp[2*i] + dx[j];
                btmp1[2*i+1] = btmp[2*i+1] + dy[j];
                ///开始节点,x,y的移动
                etmp1[2*i] = etmp[2*i] + dx[j];
                etmp1[2*i+1] = etmp[2*i+1] + dy[j];
                ///最终节点,x,y的移动
                if ( checkMove(btmp1,btmp,2*i,j) )
                {
                    bval1 = getNum(btmp1);
                    if(insert_begin(bval1,bdeep+1))
                    {
                        bque[brear] = bval1;
                        _bdeep[brear] = bdeep + 1;
                        brear++;
                    }

                }
                if ( checkMove(etmp1,etmp,2*i,j))
                {
                    eval1 = getNum(etmp1);
                    if(insert_end(eval1,edeep+1))
                    {
                        eque[erear] = eval1;
                        _edeep[erear] = edeep + 1;
                        erear++;
                    }
                }
            }
        }
        bfront++;
        efront++;///bdeep++;edeep++;
    }

}
int main()
{
#ifdef INPUT
    freopen("b:\\acm\\poj1198\\input.txt","r",stdin);
    freopen("b:\\acm\\poj1198\\output1.txt","w",stdout);
#endif
#ifdef DBG1
    int a = 1;
#endif
    while(scanf("%d",&bs[0])  != EOF)
    {
#ifdef DBG1
        printf("Process condition : %d\n",a);
        a++;
#endif
        for(int i = 1 ; i < 8 ; i++)
        {
            scanf("%d",&bs[i]);
        }
        for(int i = 0 ; i < 8 ; i++)
        {
            scanf("%d",&es[i]);
        }
        Solve();
        if (isFound)
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
    }
#ifdef INPUT
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

你可能感兴趣的:(【POJ1198 Solitaire 】 思路+解题报告+测试数据生成器)