【算法】八数码

该文章描述了一个3x3网格游戏中,数字1到8和一个x需通过交换达到特定排列的问题。使用BFS(广度优先搜索)策略,将初始网格状态转换为字符串并存储于队列,通过哈希表记录每次转换的步数,寻找到达目标排列的最小步数。提供了一段Java代码实现该算法。

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

例如:

1 2 3
x 4 6
7 5 8

在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1 2 3
4 5 6
7 8 x

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

交换过程如下:

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

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式

输入占一行,将 3×3 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3 
x 4 6 
7 5 8 

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

输出格式

输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出 −1−1。

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19

求最小步数可知用BFS

将 “3*3矩阵” 转化为 “字符串” 如

1 2 3
x 4 6
7 5 8 转化为 “123x46758”

创建队列存储每次转换后的字符串,哈希表存储成为该字符串所需要的次数(距离)。

解题代码

/**
 *
 * 在一个 3×3 的网格中,1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 3×3 的网格中。
 *
 * 例如:
 *
 * 1 2 3
 * x 4 6
 * 7 5 8
 * 在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。
 *
 * 我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
 *
 * 1 2 3
 * 4 5 6
 * 7 8 x
 * 例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。
 *
 * 交换过程如下:
 *
 * 1 2 3   1 2 3   1 2 3   1 2 3
 * x 4 6   4 x 6   4 5 6   4 5 6
 * 7 5 8   7 5 8   7 x 8   7 8 x
 * 现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
 *
 * 输入格式
 * 输入占一行,将 3×3 的初始网格描绘出来。
 *
 * 例如,如果初始网格如下所示:
 *
 * 1 2 3
 * x 4 6
 * 7 5 8
 * 则输入为:1 2 3 x 4 6 7 5 8
 *
 * 输出格式
 * 输出占一行,包含一个整数,表示最少交换次数。
 *
 * 如果不存在解决方案,则输出 −1。
 *
 * 输入样例:
 * 2 3 4 1 5 x 7 6 8
 * 输出样例
 * 19
 *
 */
public class Main {

    public static int bfs(String start) {
          Queue<String> queue =  new ArrayDeque();
          HashMap<String , Integer> distances = new HashMap<>();
          String end = "12345678x";
          queue.offer(start);
          distances.put(start,0);

          int[] dx = {0, -1, 0, 1}, dy = {1, 0, -1, 0};
          while(!queue.isEmpty()){
              String t = queue.poll();
              int distance = distances.get(t);
              if(end.equals(t)){
                  return distance;
              }
              char[] cc = t.toCharArray();
              int index = t.indexOf("x");
              int x = index / 3, y = index % 3;
              for(int i = 0; i < 4; i++){
                  int a = x + dx[i], b = y + dy[i];
                  if(a >= 0 && a < 3 && b >= 0 && b < 3){
                        //交换数字和x位置
                      char tt = cc[3 * a + b];
                      cc[index] = tt;
                      cc[3 * a + b] = 'x';
                      if(!distances.containsKey(String.valueOf(cc))){
                          distances.put(String.valueOf(cc),distance + 1);
                          queue.add(String.valueOf(cc));
                      }
                      //还原 - 留给下一个操作
                      char ttt = cc[index];
                      cc[index] = 'x';
                      cc[3 * a + b] = ttt;
                  }
              }
          }
          return -1;
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String ss = scanner.nextLine();
        String[] s = ss.split(" ");
        String start = "";
        for(int i = 0; i < s.length ; i++){
            start += s[i];
        }
        System.out.println(bfs(start));
    }
}
### A*算法八数码问题中的应用 #### 什么是A*算法? A*算法是一种启发式搜索算法,用于寻找加权图中最短路径。它结合了Dijkstra算法和贪心最佳优先搜索的特点,在每一步都评估节点的重要性并决定下一步的方向[^1]。 #### 八数码问题描述 八数码问题是经典的AI问题之一,目标是在一个3×3的棋盘上移动8个数字方块以及一个空白格子,使得初始状态变为目标状态。这个问题可以通过状态空间表示为无向图上的最短路径问题来解决。 #### 使用A*算法解决八数码问题的关键要素 为了使用A*算法解决问题,需要定义以下几个关键部分: 1. **状态表示** 每种可能的状态可以用一个长度为9的一维数组或者二维矩阵表示,其中`0`代表空白位置。例如: ```python initial_state = [ [1, 2, 3], [4, 0, 5], [7, 8, 6] ] goal_state = [ [1, 2, 3], [4, 5, 6], [7, 8, 0] ] ``` 2. **代价函数g(n)** g(n)是从起始节点到当前节点的实际代价。在这个问题中,通常将其视为步数计数器,即从初始状态到达某个特定状态所需的移动次数。 3. **启发函数h(n)** h(n)是对剩余距离的一个估计值。常用的两种方法分别是曼哈顿距离(Manhattan Distance)和欧几里得距离(Euclidean Distance)。对于八数码问题来说,更常用的是曼哈顿距离,因为它更容易计算且效果较好。 曼哈顿距离公式如下所示: \[ h(n)=\sum_{i=1}^{N}|x_i-\text{goal}_x|+|\ y_i-\text{goal}_y| \] 4. **总估价f(n)** 总估价值由实际成本加上预计未来成本构成:\[ f(n) = g(n)+h(n)\ ] 以下是基于Python实现的具体代码示例: ```python from collections import deque def manhattan_distance(state): distance = 0 for i in range(3): for j in range(3): value = state[i][j] if value != 0: target_x, target_y = divmod(value - 1, 3) distance += abs(i - target_x) + abs(j - target_y) return distance class Node: def __init__(self, state, parent=None, action=None, path_cost=0): self.state = state self.parent = parent self.action = action self.path_cost = path_cost self.heuristic = manhattan_distance(self.state) @property def total_cost(self): return self.path_cost + self.heuristic initial_state = [[1, 2, 3], [4, 0, 5], [7, 8, 6]] goal_state = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] def find_zero_position(state): for i in range(len(state)): for j in range(len(state[0])): if state[i][j] == 0: return (i, j) def generate_successors(node): successors = [] zero_pos = find_zero_position(node.state) moves = [(0, 1), (1, 0), (-1, 0), (0, -1)] for move in moves: new_x = zero_pos[0] + move[0] new_y = zero_pos[1] + move[1] if 0 <= new_x < 3 and 0 <= new_y < 3: new_state = [row[:] for row in node.state] temp = new_state[new_x][new_y] new_state[new_x][new_y] = 0 new_state[zero_pos[0]][zero_pos[1]] = temp successor_node = Node( state=new_state, parent=node, action=(move), path_cost=node.path_cost + 1 ) successors.append(successor_node) return successors frontier = [] # Priority Queue implementation required here. explored_set = set() start_node = Node(initial_state) frontier.append(start_node) solution_found = False while frontier: current_node = min(frontier, key=lambda x: x.total_cost) frontier.remove(current_node) if current_node.state == goal_state: solution_found = True break explored_set.add(tuple(map(tuple, current_node.state))) successors = generate_successors(current_node) for succ in successors: if tuple(map(tuple, succ.state)) not in explored_set: frontier.append(succ) if solution_found: steps = [] while current_node is not None: steps.insert(0, current_node.state) current_node = current_node.parent print(steps) else: print("No Solution Found.") ``` 上述程序展示了完整的解决方案流程,并利用了曼哈顿距离作为启发函数的一部分。 #### 结论 通过合理设计状态转移逻辑、选取合适的启发函数以及有效管理已访问过的节点集合等方式,我们可以高效地运用A*算法去解答诸如八数码这样的经典难题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值