📜  如何检查8个拼图的实例是否可解决?

📅  最后修改于: 2021-04-23 18:41:55             🧑  作者: Mango

什么是8拼图?
给定一个具有8个图块的3×3板(每个图块都有一个1到8的数字)和一个空白空间。目的是使用空白空间按顺序将数字放置在图块上。我们可以将四个相邻的(左,右,上方和下方)磁贴滑动到空白区域。

8个拼图

如何确定给定状态是否可解决?
以下是两个示例,第一个示例可以通过一系列幻灯片达到目标状态。第二个例子不能。

8拼图

以下是检查8个拼图是否可解决的简单规则。
如果在输入状态下反转数为奇数,则不可能求解8个难题的实例。在上图中给出的示例中,第一个示例具有10个反演,因此可以求解。第二个示例有11个反演,因此无法解决。
什么是反演?
如果图块上的值与它们在目标状态下出现的顺序相反,则一对图块将形成反转。例如,以下8个谜题的实例具有两个反转,(8,6)和(8,7)。

1   2   3
   4   _   5
   8   6   7      

以下是检查8个拼图的给定实例是否可解决的实现。这个想法很简单,我们在给定的8个谜题中计算倒置数。

C++
// C++ program to check if a given instance of 8 puzzle is solvable or not
#include 
using namespace std;
 
// A utility function to count inversions in given array 'arr[]'
int getInvCount(int arr[])
{
    int inv_count = 0;
    for (int i = 0; i < 9 - 1; i++)
        for (int j = i+1; j < 9; j++)
             // Value 0 is used for empty space
             if (arr[j] && arr[i] &&  arr[i] > arr[j])
                  inv_count++;
    return inv_count;
}
 
// This function returns true if given 8 puzzle is solvable.
bool isSolvable(int puzzle[3][3])
{
    // Count inversions in given 8 puzzle
    int invCount = getInvCount((int *)puzzle);
 
    // return true if inversion count is even.
    return (invCount%2 == 0);
}
 
/* Driver progra to test above functions */
int main(int argv, int** args)
{
  int puzzle[3][3] =  {{1, 8, 2},
                      {0, 4, 3},  // Value 0 is used for empty space
                      {7, 6, 5}};
  isSolvable(puzzle)? cout << "Solvable":
                      cout << "Not Solvable";
  return 0;
}


Java
// Java program to check if a given
// instance of 8 puzzle is solvable or not
class GFG
{
     
// A utility function to count
// inversions in given array 'arr[]'
static int getInvCount(int[][] arr)
{
    int inv_count = 0;
    for (int i = 0; i < 3 - 1; i++)
        for (int j = i + 1; j < 3; j++)
         
            // Value 0 is used for empty space
            if (arr[j][i] > 0 &&
                            arr[j][i] > arr[i][j])
                inv_count++;
    return inv_count;
}
 
// This function returns true
// if given 8 puzzle is solvable.
static boolean isSolvable(int[][] puzzle)
{
    // Count inversions in given 8 puzzle
    int invCount = getInvCount(puzzle);
 
    // return true if inversion count is even.
    return (invCount % 2 == 0);
}
 
/* Driver code */
public static void main (String[] args)
{
    int[][] puzzle = {{1, 8, 2},{0, 4, 3},{7, 6, 5}};
    if(isSolvable(puzzle))
        System.out.println("Solvable");
    else
    System.out.println("Not Solvable");
}
}
 
// This code is contributed by chandan_jnu


Python3
# Python3 program to check if a given
# instance of 8 puzzle is solvable or not
 
# A utility function to count
# inversions in given array 'arr[]'
def getInvCount(arr) :
 
    inv_count = 0
    for i in range(0, 2) :
        for j in range(i + 1, 3) :
           
            # Value 0 is used for empty space
            if (arr[j][i] > 0 and arr[j][i] > arr[i][j]) :
                inv_count += 1
    return inv_count
     
# This function returns true
# if given 8 puzzle is solvable.
def isSolvable(puzzle) :
 
    # Count inversions in given 8 puzzle
    invCount = getInvCount(puzzle)
   
    # return true if inversion count is even.
    return (invCount % 2 == 0)
     
    # Driver code
puzzle = [[1, 8, 2],[0, 4, 3],[7, 6, 5]]
if(isSolvable(puzzle)) :
    print("Solvable")
else :
    print("Not Solvable")
     
    # This code is contributed by divyeshrabadiya07.


C#
// C# program to check if a given
// instance of 8 puzzle is solvable or not
using System;
 
class GFG
{
     
// A utility function to count
// inversions in given array 'arr[]'
static int getInvCount(int[,] arr)
{
    int inv_count = 0;
    for (int i = 0; i < 3 - 1; i++)
        for (int j = i + 1; j < 3; j++)
         
            // Value 0 is used for empty space
            if (arr[j, i] > 0 && arr[j, i] > arr[i, j])
                inv_count++;
    return inv_count;
}
 
// This function returns true
// if given 8 puzzle is solvable.
static bool isSolvable(int[,] puzzle)
{
    // Count inversions in given 8 puzzle
    int invCount = getInvCount(puzzle);
 
    // return true if inversion count is even.
    return (invCount % 2 == 0);
}
 
/* Driver code */
static void Main()
{
    int[,] puzzle = new int[3,3]{{1, 8, 2},
                            {0, 4, 3}, // Value 0 is used for empty space
                            {7, 6, 5}};
    if(isSolvable(puzzle))
        Console.WriteLine("Solvable");
    else
       Console.WriteLine("Not Solvable");
}
}
 
// This code is contributed by chandan_jnu


PHP


Javascript


输出 :

Solvable

注意,上面的实现使用简单的算法进行反转计数。这样做是为了简化。使用基于合并排序的算法对反转计数,可以将代码优化为O(nLogn)。
这是如何运作的?
该思想基于以下事实:一组移动后反转的奇偶性保持不变,即,如果初始阶段的反转计数为奇数,则在任何移动序列后反转的奇偶性仍为奇数,并且如果反转计数为偶数,则它即使经过任何顺序的移动,仍然保留。在目标状态下,存在0个反转。因此,我们只能从具有反转计数的状态达到目标状态。
反转计数的奇偶性如何不变?
滑动图块时,要么行移动(将左图块或右图块移动到空白空间),要么使列移动(将上图块或下图块移动到空白空间)。
a)行移动不会改变反转计数。请参阅以下示例

1   2   3    Row Move     1   2   3
   4   _   5   ---------->   _   4   5 
   8   6   7                 8   6   7
  Inversion count remains 2 after the move

   1   2   3    Row Move     1   2   3
   4   _   5   ---------->   4   5   _
   8   6   7                 8   6   7
  Inversion count remains 2 after the move

b)列移动执行以下三个操作之一。
…..(i)将反转计数增加2。请参见以下示例。

1   2   3   Column Move     1   _   3
         4   _   5   ----------->    4   2   5  
         8   6   7                   8   6   7
      Inversion count increases by 2 (changes from 2 to 4)
       

…..(ii)反转计数减少2

1   3   4    Column Move     1   3   4
         5   _   6   ------------>    5   2   6
         7   2   8                    7   _   8
      Inversion count decreases by 2 (changes from 5  to 3)

…..(iii)保持反转计数相同。

1   2   3    Column Move     1   2   3
         4   _   5   ------------>    4   6   5
         7   6   8                    7   _   8
        Inversion count remains 1 after the move