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;}
- Sicily 1151. 魔板
- Sicily 1151. 魔板
- [sicily online]1151. 魔板
- Sicily 1151. 魔板
- Sicily 1151. 魔板
- Sicily 1151. 魔板
- [sicily]1151. 魔板
- Sicily 1151. 魔板[Speical judge]
- [sicily]1151. 魔板[Special judge]
- [sicily]1151. 魔板[Special judge]
- sicily 1150. 简单魔板 & 1151. 魔板
- sicily 1150,1151.魔版
- sicily 1151 魔板
- Sicily 1151 魔板 Another Thought
- Sicily 1151. 魔板解题报告
- sicily 1150&1151 魔板[Special judge]
- Sicily 1027
- sicily 1007
- How to make Decision Effectively
- Openstack的命令
- Linux和Windows共享文件的实现
- 数据结构(二)算法绪论
- goagent
- Sicily 1151. 魔板
- Oracle 分页sql
- linux关掉cpu命令
- 查找字符串中第一个只出现一次的字符
- 线程
- SQL Server之分布式事务
- week5 Neural Network Learning
- 查找算法 之 二叉查找树
- jsp学习一