1151 魔板

1151 魔板

sicily algorithm 广搜 康托展开



原题中文描述

原题链接

魔板由8个大小相同方块组成,分别用涂上不同颜色,用1到8的数字表示。 
其初始状态是 
1 2 3 4 
8 7 6 5 
对魔板可进行三种基本操作: 
A操作(上下行互换): 
8 7 6 5 
1 2 3 4 
B操作(每次以行循环右移一个): 
4 1 2 3 
5 8 7 6 
C操作(中间四小块顺时针转一格): 
1 7 2 4 
8 6 3 5 
用上述三种基本操作,可将任一种状态装换成另一种状态。


输入

输入包括多个要求解的魔板,每个魔板用三行描述。 
第一行步数N,表示最多容许的步数. 
第二、第三行表示目标状态,按照魔板的形状,颜色用1到8的表示。 
当N等于-1的时候,表示输入结束.


输出

对于每一个要求解的魔板,输出一行. 
首先是一个整数M,表示你找到解答所需要的步数。接着若干个空格之后,从第一步开始按顺序给出M步操作(每一步是A、B或C),相邻两个操作之间没有任何空格. 
注意:如果不能达到,则M输出-1即可.


算法思想

  • 广搜 - bfs
  • 枚举所有可能的三种操作
  • 利用康托展开来唯一标识每一个转移status
  • 判重
  • 第一个可行解就是最优解

至于康托展开的理解, 可以看下我写的一篇博文


数据结构

用数组变量来保存每次魔板转移操作的信息

 
 
  1. const int M = 40330 ;
  2. const int N = 8 ;
  3. // 存储康托展开的个数, 即魔板方块的个数的阶乘
  4. int ktNums[9] = {0 , 1 , 2 , 6 , 24 , 120 , 720 , 5040 , 40320} ;
  5. // 记录魔板是否转移
  6. bool visited[M] ;
  7. // 存储每次转移的操作
  8. string str[M] ;

解题思路及算法描述

  1. 初始化搜索路径的init status及对应的变量visited(为bool数组, 取值为false), 字符串数组变量str为空,
  2. 进行bfs搜索: 
    2.1记录init status s, 将其放进列表, 并计算其的康托值ktVal, 设visited[ktVal]设为true 
    2.2定义一个列表list<int*> q ;, 将s放进q里(入列) 
    2.3进行广搜while(!q.empty())
 
 
  1. 2.3.1 获取列表`q`的队首`f = q.front()`, 并删除队首`q.pop_front()`
  2. 2.3.2 `f`进行`A,B,C,`三种操作, 得到对应的`a,b,c`转移状态
  3. 2.3.3 如果`a,b,c`还没有被访问, 则分别将其放入队列`q`, 并计算其的康托值`ktVal`和设置其对应的状态`visited[ktVal]``true`, 否则不再扩展
  4. 2.3.4 `str`记录转移过程
  5. 2.3.5 直到循环条件结束

3.广搜结束

这里只需一次广搜所有可能的转移转移操作及其对应的转移status, 因为每次搜索的开始status是一样的.


code

 
 
  1. // Problem#: 1151
  2. #include <iostream>
  3. #include <string>
  4. #include <queue>
  5. #include <set>
  6. #include <list>
  7. using namespace std ;
  8. const int M = 40330 ;
  9. const int N = 8 ;
  10. int ktNums[9] = {0 , 1 , 2 , 6 , 24 , 120 , 720 , 5040 , 40320} ;
  11. bool visited[M] ;
  12. string str[M] ;
  13. int bf[N] ;
  14. // 康托展开
  15. int cator(int q[]) {
  16. int i, j ;
  17. int result = 0 ;
  18. for (i = 0 ; i < N - 1 ; i++) {
  19. int count = 0 ;
  20. for (j = i + 1 ; j < N ; j++)
  21. if (q[j] < q[i])
  22. count++ ;
  23. result += count * ktNums[N - i - 1] ;
  24. }
  25. return result ;
  26. }
  27. bool isEqual(int *a , int *b) {
  28. for(int i = 0 ; i < N ; i++) {
  29. if(a[i] != b[i]) {
  30. return 0 ;
  31. }
  32. }
  33. return 1 ;
  34. }
  35. // init the start status
  36. void inni(int *a) {
  37. a[0] = 1 ; a[1] = 2 ;
  38. a[2] = 3 ; a[3] = 4 ;
  39. a[4] = 8 ; a[5] = 7 ;
  40. a[6] = 6 ; a[7] = 5 ;
  41. }
  42. void A(int *a ,int *b) {
  43. b[0] = a[4] ; b[1] = a[5] ;
  44. b[2] = a[6] ; b[3] = a[7] ;
  45. b[4] = a[0] ; b[5] = a[1] ;
  46. b[6] = a[2] ; b[7] = a[3] ;
  47. }
  48. void B(int *a ,int *b) {
  49. b[1] = a[0] ; b[2] = a[1] ;
  50. b[3] = a[2] ; b[0] = a[3] ;
  51. b[5] = a[4] ; b[6] = a[5] ;
  52. b[7] = a[6] ; b[4] = a[7] ;
  53. }
  54. void C(int *a ,int *b) {
  55. b[0] = a[0] ; b[3] = a[3] ;
  56. b[4] = a[4] ; b[7] = a[7] ;
  57. b[1] = a[5] ; b[2] = a[1] ;
  58. b[6] = a[2] ; b[5] = a[6] ;
  59. }
  60. void bfs() {
  61. list<int*> q ;
  62. int T , tt ;
  63. int *f = new int[N] ;
  64. // int *f;
  65. // init
  66. memset(visited, false, sizeof(visited));
  67. inni(f) ;
  68. q.push_back(f) ;
  69. visited[cator(f)] = 1 ;
  70. while(!q.empty()) {
  71. int *fa = new int[N] ;
  72. int *fb = new int[N] ;
  73. int *fc = new int[N] ;
  74. // get the front status from the list
  75. f = q.front() ;
  76. // then pop and never visit it
  77. q.pop_front() ;
  78. if(isEqual(f , bf))
  79. break ;
  80. // compute the correspond kt value
  81. tt = cator(f) ;
  82. // A operation
  83. A(f , fa) ;
  84. // compute the kt value
  85. T = cator(fa) ;
  86. // if not visited, visit it
  87. if(!visited[T]) {
  88. q.push_back(fa) ;
  89. visited[T] = true ;
  90. str[T] = str[tt] + "A" ;
  91. }
  92. // B operation
  93. B(f , fb) ;
  94. // compute the correspond kt value
  95. T = cator(fb) ;
  96. // if not visited, visit it
  97. if(!visited[T]) {
  98. q.push_back(fb) ;
  99. visited[T] = true ;
  100. str[T] = str[tt] + "B" ;
  101. }
  102. // C operation
  103. C(f , fc) ;
  104. // compute the correspond kt value
  105. T = cator(fc) ;
  106. // if not visited, visit it
  107. if(!visited[T]) {
  108. q.push_back(fc) ;
  109. visited[T] = true ;
  110. str[T] = str[tt] + "C" ;
  111. }
  112. }
  113. }
  114. int main() {
  115. int t , k ;
  116. // the start status
  117. int g[N] ;
  118. inni(g) ;
  119. // because each case start from the same status
  120. // so just bfs() one time
  121. bfs() ;
  122. while(cin >> t , t != -1) {
  123. for(int i = 0 ; i < N ; i++){
  124. cin >> bf[i] ;
  125. }
  126. // determin the target status is equal to
  127. // the initial status
  128. if(isEqual(bf , g)) {
  129. cout << 0 << endl ;
  130. } else {
  131. // get kt value
  132. k = cator(bf) ;
  133. // determine whether reach the target status
  134. if(!str[k].empty() && str[k].size() <= t) {
  135. cout << str[k].size() << ' ' << str[k] << endl ;
  136. } else {
  137. cout << -1 << endl ;
  138. }
  139. }
  140. }
  141. return 0 ;
  142. }

测试数据

下面给出4给样例, 其中每个样例的第一行表示允许操作的步长, 第二行表示输入的target status, 第三行表示输出结果(按题意输出) 
1.


3 2 4 7 1 6 8 5 
-1

2.


3 5 4 7 1 6 8 2 
-1

3.

16 
5 8 7 6 4 1 2 3 
AB

4.

20 
2 4 3 7 1 6 8 5 
CBBCBCBCBCBCBCB

5

-1 
end


时间复杂度分析

总体来说, 对其进行判重后, 时间复杂度为O(n!), 其中n为魔板方块的个数. 
(因为对初始status或者中间某个status进行三种转移操作后, 其输出的status不外乎是魔板的全排列中的一个)


end

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值