Sicily 1151. 魔板

来源:互联网 发布:mac 系统默认编码格式 编辑:程序博客网 时间:2024/05/03 21:23

题目大意:

类似于华容道的按规则移动方块,达到预期状态。

算法思想及主要数据结构:

BFS(广搜)+ 康托展开 + Queue

1、BFS

I.   将初始状态放入队列,

II.  得到队列中的第一个状态,则从队列中pop

III. 得到pop出的状态与目标太进行比较,如果匹配则结束,否则对当前状态继续分别进行A、B、C三种操作,把得到的新状态依次放入队列。

IV.只要队列不为空,重复II操作。

 2、康托展开

康托展开是一种特殊的哈希函数,它的使用范围是对于n个数的排列进行状态的压缩和存储,例如要对9的全排列进行判重.没有必要开一个10^9的数组,同时内存也不允许开到那么大的数组.对此,有人提出了优化,即对于一个n的排列数,没有必要开到10^n,因为在一个排列中每个数只出现一次,所以只要前n-1位确定了,前N位就确定了。

但是以上的想法仍不是可行的,因为N可以很大,例如15,所以便引入了康托展开:只需要确定这个排列在总的排列情况中是第几小的就可以了。

例如: {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312321 。

代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。

他们间的对应关系可由康托展开来找到。

如我想知道321是{1,2,3}中第几个小的数可以这样考虑 :

第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个小的数。 2*2!+1*1!+0*0!就是康托展开。

再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个小数。

3、A、B、C操作的数学方法

A操作(结果存入m,n变量中)

after.x= curr.y;

after.y= curr.x;

B操作

after.x= after.x + curr.x[3]+curr.x[0]+curr.x[1]+curr.x[2]; 

after.y= after.y + curr.y[3]+curr.y[0]+curr.y[1]+curr.y[2]; 

C操作:

after.x = after.x +curr.x[0]+curr.y[1]+curr.x[1]+curr.x[3]; 

after.y = after.y +curr.y[0]+curr.y[2]+curr.x[2]+curr.y[3]; 

 

#include <iostream>#include <cstring>#include <queue> using namespace std;struct Record{    string x,y;    string op;   }start,target;const int PermSize = 8;int factory[] = { 1, 1, 2, 6, 24, 120,720, 5040 }; // 7的阶乘(n-1的阶乘) bool isVisited[50000];void initial(){        string tmp = "";                 // 初始化初态魔板     start.x = "1234";    start.y = "8765";    start.op = "";        target.x = "";    target.y = "";          for(int i = 0;i < 4;i++){         cin >> tmp;        target.x += tmp;      }      for(int j = 0;j < 4;j++){         cin >> tmp;        target.y += tmp;      }          memset(isVisited, false ,sizeof(isVisited)); }int cantor( string buf ) {    int i, j, counted;    int result = 0;    for ( i = 0; i < PermSize; ++i )     {        counted = 0;        for( j = i + 1; j < PermSize + 1; ++j )            if( buf[i] > buf[j] )                ++counted;        result = result + counted * factory[PermSize - i - 1];    }    return result;}bool isEqual(const Record &A,const Record &B){    return ( A.x == B.x && A.y == B.y ); }Record operate(Record curr, Record after,int operation)  {      switch(operation){        case 0:             after.x = curr.y;             after.y = curr.x;              after.op += "A";            break;          case 1:             after.x = after.x + curr.x[3]+curr.x[0]+curr.x[1]+curr.x[2];              after.y = after.y + curr.y[3]+curr.y[0]+curr.y[1]+curr.y[2];              after.op += "B";             break;        case 2:             after.x = after.x + curr.x[0]+curr.y[1]+curr.x[1]+curr.x[3];              after.y = after.y + curr.y[0]+curr.y[2]+curr.x[2]+curr.y[3];              after.op += "C";            break;      }    return after;  }  // 广搜 void bfs( int m ){    if( isEqual(start,target) ){         cout << 0 << endl;        return;    }    queue<Record> records;    records.push(start);        // 队列不为空     while( !records.empty() ){        Record curr = records.front();        records.pop();                // 如果操作次数大于要求次数则返回-1         if( curr.op.size() > m ) {            cout << -1 << endl;              return;        }                // 当前态与目标太匹配则返回         if( isEqual(curr, target) ){            cout << curr.op.size() << " " << curr.op << endl;             return;        }                // 循环进行A,B,C操作         for( int i = 0; i < 3; i++ ){            Record after;            after.x = "";             after.y ="";             after.op = curr.op;                        after = operate(curr,after,i);                                    int cn = cantor( after.x + after.y );             if( !isVisited[ cn ]){                isVisited[ cn ] = true;                records.push(after);            }            }                                  }       } int main(){        int m;        while( cin >> m && m != -1 ){        initial();        bfs(m);    }                return 0;}




原创粉丝点击