• 八数码—unordered哈希表即记录步骤—A*算法—曼哈顿距离—BFS


    在一个 3×3 的网格中,1∼8 这 8 个数字和一个 X 恰好不重不漏地分布在这 3×3的网格中。

    例如:

    1. 1 2 3
    2. X 4 6
    3. 7 5 8

    在游戏过程中,可以把 X 与其上、下、左、右四个方向之一的数字交换(如果存在)。我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

    1. 1 2 3
    2. 4 5 6
    3. 7 8 X

    例如,示例中图形就可以通过让 X 先后与右、下、右三个方向的数字交换成功得到正确排列。交换过程如下:

    1. 1 2 3 1 2 3 1 2 3 1 2 3
    2. X 4 6 4 X 6 4 5 6 4 5 6
    3. 7 5 8 7 5 8 7 X 8 7 8 X

    X 与上下左右方向数字交换的行动记录为 udlr。现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。

    输入格式

    输入占一行,将 3×3的初始网格描绘出来。例如,如果初始网格如下所示:

    1. 1 2 3
    2. x 4 6
    3. 7 5 8

    则输入为:1 2 3 x 4 6 7 5 8

    输出格式

    输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。如果答案不唯一,输出任意一种合法方案即可。如果不存在解决方案,则输出 unsolvable

    输入样例:

    2  3  4  1  5  x  7  6  8 
    

    输出样例

    ullddrurdllurdruldr
    

     

     分析:如果逆序对数量是奇数则无解;则双层for循环判断即可;

    A*写法

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. // 估计函数,计算至少还要几步才能到target board。
    8. // 每次移动一步只能让水平或者竖直方向挪动一格。
    9. // 所以对比当前board和target board,看看每一位和目标位差多少曼哈顿距离。
    10. // 注意这里x不算,所以每次移动只能把非x能往正确的位置移动一步。
    11. // 当所有数字都到位了,x也自然到位。
    12. int f(string state)//求曼哈顿距离
    13. {
    14. int res = 0;
    15. for (int i = 0; i < state.size(); i++) {
    16. if (state[i] == 'x')
    17. continue;
    18. //数字应该在的位置
    19. int x = i / 3;
    20. int y = i % 3;
    21. //数字的当前位置
    22. int nx = (state[i] - '1') / 3;
    23. int ny = (state[i] - '1') % 3;
    24. res += abs(x - nx) + abs(y - ny);
    25. }
    26. return res;
    27. }
    28. string bfs(string start)
    29. {
    30. int dx[4] = { -1, 0, 1, 0 }, dy[4] = { 0, 1, 0, -1 };
    31. char op[4] = { 'u', 'r', 'd', 'l' };
    32. string end = "12345678x";
    33. unordered_mapint> dist;//存真实距离
    34. unordered_mapchar>> pre;//存步骤
    35. priority_queueint, string>, vectorint, string>>, greaterint, string>>> q;//存估计距离+当前状态
    36. q.push({ f(start)+0, start });//估计值=从起点到这个点的真实距离加上到终点的估计距离。
    37. dist[start] = 0;
    38. while (q.size())
    39. {
    40. auto t = q.top();
    41. q.pop();
    42. //当前操作的字符串
    43. string now = t.second;
    44. //出口
    45. if (now == end) break;
    46. //真实最短距离
    47. int x, y;
    48. for (int i = 0; i < now.size(); i++)
    49. if (now[i] == 'x')
    50. {
    51. x = i / 3, y = i % 3;
    52. break;
    53. }
    54. for (int i = 0; i < 4; i++)
    55. {
    56. int a = x + dx[i], b = y + dy[i];
    57. if (a >= 0 && a < 3 && b >= 0 && b < 3)
    58. {
    59. swap(now[x * 3 + y], now[a * 3 + b]);
    60. string next = now;
    61. swap(now[x * 3 + y], now[a * 3 + b]);
    62. if (!dist.count(next) || dist[next]>dist[now]+1)
    63. //这是A*算法,除了终点之外,当第一次搜到某个点的时候,距离不一定是最短的,
    64. //其dist[]的值是不断变小的,所以要加后一个判断。
    65. //dis[now]+f(now);只要估计距离小于或者等于真实距离,
    66. //那么状态now第一次从队列中出来的时候它的距离就一定是最短距离了;
    67. {
    68. dist[next] = dist[now] + 1;
    69. pre[next] = { now, op[i] };
    70. q.push({ dist[next] + f(next), next });//当前的真实植加上估计值
    71. }
    72. }
    73. }
    74. }
    75. string res;
    76. //输出步数
    77. while (end != start)
    78. {
    79. res += pre[end].second;
    80. end = pre[end].first;
    81. }
    82. reverse(res.begin(), res.end());
    83. return res;
    84. }
    85. int main()
    86. {
    87. string g, c, seq;
    88. while (cin >> c)
    89. {
    90. g += c;
    91. if (c != "x") seq += c;
    92. }
    93. //判断无解情况;
    94. int t = 0;
    95. for (int i = 0; i < seq.size(); i++)
    96. for (int j = i + 1; j < seq.size(); j++)
    97. if (seq[i] > seq[j])
    98. t++;
    99. if (t % 2) puts("unsolvable");
    100. else cout << bfs(g) << endl;
    101. return 0;
    102. }

    简单BFS写法

    1. #include
    2. #define endl '\n'
    3. #define int long long
    4. #include
    5. #define Bug cout<<"---------------------"<
    6. using namespace std;
    7. constexpr int N = 10;
    8. char arr[N];
    9. string ed = "12345678x";
    10. string st;
    11. //数据结构
    12. //存步数
    13. //unordered_mapdis;
    14. unordered_mapchar, string> >pre;//存步骤
    15. int dx[4] = { -1,0,0,1 }; int dy[4] = { 0,1,-1,0 };//分别对应上,右,左,下
    16. void bfs() {
    17. queueq;
    18. q.push(st);
    19. //dis[st] = 0;
    20. while (q.size()) {
    21. string now = q.front();
    22. q.pop();
    23. //if (now == ed) return dis[now];
    24. if (now == ed) return ;
    25. int pos = now.find('x');
    26. int x = pos / 3, y = pos % 3;
    27. for (int i = 0;i < 4;i++) {
    28. int tx = x + dx[i];
    29. int ty = y + dy[i];
    30. if (tx >= 3 || tx < 0 || ty < 0 || ty >= 3) {
    31. continue;
    32. }
    33. swap(now[pos], now[tx * 3 + ty]);
    34. string next = now;
    35. swap(now[pos], now[tx * 3 + ty]);
    36. if (!pre.count(next)) {
    37. //dis[next] = dis[now] + 1;
    38. if (i == 0) {
    39. pre[next] = { 'u',now };
    40. }
    41. else if (i == 1) {
    42. pre[next] = { 'r',now };
    43. }
    44. else if (i == 2) {
    45. pre[next] = { 'l',now };
    46. }
    47. else{
    48. pre[next] = { 'd',now };
    49. }
    50. q.push(next);
    51. }
    52. }
    53. }
    54. }
    55. signed main() {
    56. ios_base::sync_with_stdio(0);
    57. cin.tie(0); cout.tie(0);
    58. //读入
    59. string x;
    60. for (int i = 0;i < 9;i++) {
    61. char c;
    62. cin >> c;
    63. st += c;
    64. if (c != 'x') x += c;
    65. }
    66. int cnt = 0;//统计逆序对的数量
    67. for (int i = 0;i < 8;i++)
    68. for (int j = i + 1;j < 8;j++)
    69. if (x[i] > x[j])
    70. cnt++;
    71. if (cnt % 2)
    72. {
    73. printf("unsolvable\n");//如果逆序对为奇数,就不可能抵达终点
    74. return 0;
    75. }
    76. //搜索
    77. bfs();
    78. //输出最小步数
    79. //cout << ans << endl;
    80. //使用递推出结果
    81. string res;
    82. while (st != ed) {
    83. res += pre[ed].first;
    84. ed = pre[ed].second;
    85. }
    86. //是从结尾开始找,所以需要反转一下
    87. reverse(res.begin(), res.end());
    88. cout << res << endl;
    89. return 0;
    90. }

     

  • 相关阅读:
    RAID5的配置流程及模拟硬盘损坏
    SQL中为什么不要使用1=1?
    SSH协议简介与使用
    4.RabbitMQ高级特性
    代码随想录day48|198. 打家劫舍213. 打家劫舍 II337. 打家劫舍 III
    STM32程序(移植)中头文件的路径
    【GIS】地理坐标系与投影坐标系的区别
    幽默直观的文档作者注释
    P6774 [NOI2020] 时代的眼泪(分块)
    Java 中 Method 和 MethodSignature 区别
  • 原文地址:https://blog.csdn.net/m0_72522122/article/details/127719607