📜  8个难题的问题,使用分支和绑定

📅  最后修改于: 2021-04-29 13:36:39             🧑  作者: Mango

我们在下面的文章中介绍了Branch和Bound,并讨论了0/1背包问题。

  • 分界线|套装1(带0/1背包的简介)
  • 分界线|第2组(0/1背包的实施)

在这个难题解决方案中,讨论了8个难题。
给定一个具有8个图块的3×3板(每个图块都有一个1到8的数字)和一个空白空间。目的是将数字放置在图块上,以使用空白空间匹配最终配置。我们可以将四个相邻的(左,右,上方和下方)磁贴滑动到空白区域。
例如,

8拼图

1. DFS(蛮力)
我们可以在状态空间(给定问题的所有配置,即可以从初始状态到达的所有状态的集合)上进行深度优先搜索。

图片(6)

8拼图的州立空间树

在这种解决方案中,连续的动作可以使我们远离目标,而不是使我们离目标更近。无论初始状态如何,对状态空间树的搜索都遵循从根到最左边的路径。在这种方法中可能永远找不到答案节点。
2. BFS(蛮力)
我们可以在状态空间树上执行广度优先搜索。这总是找到最接近根的目标状态。但是,无论初始状态是什么,该算法都会尝试像DFS一样的顺序移动。
3.分支定界
通常可以通过使用“智能”排名函数(也称为近似成本函数)来加快对答案节点的搜索,以避免在不包含答案节点的子树中进行搜索。它类似于回溯技术,但使用类似BFS的搜索。
分支和绑定基本上涉及三种类型的节点
1.活动节点是已生成但尚未生成其子节点的节点。
2.电子节点是一个活动节点,当前正在探索其子节点。换句话说,E节点是当前正在扩展的节点。
3.死节点是生成的节点,将不再扩展或探索。死节点的所有子节点均已扩展。
成本函数:
搜索树中的每个节点X与一个成本相关联。成本函数对于确定下一个电子节点很有用。下一个电子节点是成本最低的电子节点。成本函数定义为

C(X) = g(X) + h(X) where
   g(X) = cost of reaching the current node 
          from the root
   h(X) = cost of reaching an answer node from X.

8难题算法的理想成本函数
我们假设在任何方向上移动一个图块的成本为1。请记住,我们为8难题算法定义了一个成本函数,如下所示:

c(x) = f(x) + h(x) where
   f(x) is the length of the path from root to x 
        (the number of moves so far) and
   h(x) is the number of non-blank tiles not in 
        their goal position (the number of mis-
        -placed tiles). There are at least h(x) 
        moves to transform state x to a goal state

有一种算法可用于获取h(x)的近似值,该近似值是未知值。
完整算法:

/* Algorithm LCSearch uses c(x) to find an answer node
 * LCSearch uses Least() and Add() to maintain the list 
   of live nodes
 * Least() finds a live node with least c(x), deletes
   it from the list and returns it
 * Add(x) adds x to the list of live nodes
 * Implement list of live nodes as a min-heap */

struct list_node
{
   list_node *next;

   // Helps in tracing path when answer is found
   list_node *parent; 
   float cost;
} 

algorithm LCSearch(list_node *t)
{
   // Search t for an answer node
   // Input: Root node of tree t
   // Output: Path from answer node to root
   if (*t is an answer node)
   {
       print(*t);
       return;
   }
   
   E = t; // E-node

   Initialize the list of live nodes to be empty;
   while (true)
   {
      for each child x of E
      {
          if x is an answer node
          {
             print the path from x to t;
             return;
          }
          Add (x); // Add x to list of live nodes;
          x->parent = E; // Pointer for path to root
      }

      if there are no more live nodes
      {
         print ("No answer node");
         return;
      }
       
      // Find a live node with least estimated cost
      E = Least(); 

      // The found node is deleted from the list of 
      // live nodes
   }
}

下图显示了上述算法从8拼图的给定初始配置到达最终配置所遵循的路径。注意,仅具有最小成本函数值的节点被扩展。

C++14
// Program to print path from root node to destination node
// for N*N -1 puzzle algorithm using Branch and Bound
// The solution assumes that instance of puzzle is solvable
#include 
using namespace std;
#define N 3
 
// state space tree nodes
struct Node
{
    // stores the parent node of the current node
    // helps in tracing path when the answer is found
    Node* parent;
 
    // stores matrix
    int mat[N][N];
 
    // stores blank tile coordinates
    int x, y;
 
    // stores the number of misplaced tiles
    int cost;
 
    // stores the number of moves so far
    int level;
};
 
// Function to print N x N matrix
int printMatrix(int mat[N][N])
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
            printf("%d ", mat[i][j]);
        printf("\n");
    }
}
 
// Function to allocate a new node
Node* newNode(int mat[N][N], int x, int y, int newX,
              int newY, int level, Node* parent)
{
    Node* node = new Node;
 
    // set pointer for path to root
    node->parent = parent;
 
    // copy data from parent node to current node
    memcpy(node->mat, mat, sizeof node->mat);
 
    // move tile by 1 position
    swap(node->mat[x][y], node->mat[newX][newY]);
 
    // set number of misplaced tiles
    node->cost = INT_MAX;
 
    // set number of moves so far
    node->level = level;
 
    // update new blank tile cordinates
    node->x = newX;
    node->y = newY;
 
    return node;
}
 
// bottom, left, top, right
int row[] = { 1, 0, -1, 0 };
int col[] = { 0, -1, 0, 1 };
 
// Function to calculate the number of misplaced tiles
// ie. number of non-blank tiles not in their goal position
int calculateCost(int initial[N][N], int final[N][N])
{
    int count = 0;
    for (int i = 0; i < N; i++)
      for (int j = 0; j < N; j++)
        if (initial[i][j] && initial[i][j] != final[i][j])
           count++;
    return count;
}
 
// Function to check if (x, y) is a valid matrix cordinate
int isSafe(int x, int y)
{
    return (x >= 0 && x < N && y >= 0 && y < N);
}
 
// print path from root node to destination node
void printPath(Node* root)
{
    if (root == NULL)
        return;
    printPath(root->parent);
    printMatrix(root->mat);
 
    printf("\n");
}
 
// Comparison object to be used to order the heap
struct comp
{
    bool operator()(const Node* lhs, const Node* rhs) const
    {
        return (lhs->cost + lhs->level) > (rhs->cost + rhs->level);
    }
};
 
// Function to solve N*N - 1 puzzle algorithm using
// Branch and Bound. x and y are blank tile coordinates
// in initial state
void solve(int initial[N][N], int x, int y,
           int final[N][N])
{
    // Create a priority queue to store live nodes of
    // search tree;
    priority_queue, comp> pq;
 
    // create a root node and calculate its cost
    Node* root = newNode(initial, x, y, x, y, 0, NULL);
    root->cost = calculateCost(initial, final);
 
    // Add root to list of live nodes;
    pq.push(root);
 
    // Finds a live node with least cost,
    // add its childrens to list of live nodes and
    // finally deletes it from the list.
    while (!pq.empty())
    {
        // Find a live node with least estimated cost
        Node* min = pq.top();
 
        // The found node is deleted from the list of
        // live nodes
        pq.pop();
 
        // if min is an answer node
        if (min->cost == 0)
        {
            // print the path from root to destination;
            printPath(min);
            return;
        }
 
        // do for each child of min
        // max 4 children for a node
        for (int i = 0; i < 4; i++)
        {
            if (isSafe(min->x + row[i], min->y + col[i]))
            {
                // create a child node and calculate
                // its cost
                Node* child = newNode(min->mat, min->x,
                              min->y, min->x + row[i],
                              min->y + col[i],
                              min->level + 1, min);
                child->cost = calculateCost(child->mat, final);
 
                // Add child to list of live nodes
                pq.push(child);
            }
        }
    }
}
 
// Driver code
int main()
{
    // Initial configuration
    // Value 0 is used for empty space
    int initial[N][N] =
    {
        {1, 2, 3},
        {5, 6, 0},
        {7, 8, 4}
    };
 
    // Solvable Final configuration
    // Value 0 is used for empty space
    int final[N][N] =
    {
        {1, 2, 3},
        {5, 8, 6},
        {0, 7, 4}
    };
 
    // Blank tile coordinates in initial
    // configuration
    int x = 1, y = 2;
 
    solve(initial, x, y, final);
 
    return 0;
}


输出 :

1 2 3 
5 6 0 
7 8 4 

1 2 3 
5 0 6 
7 8 4 

1 2 3 
5 8 6 
7 0 4 

1 2 3 
5 8 6 
0 7 4