構造數獨

編程之美有一道關於深度搜索和回溯應用的題目——構造數獨:
數獨的棋盤是由九九八十一個小方格組成的。玩家在每個小格子中,分別天上1至9的任意一個數字,讓整個棋盤每一行,每一列,以及每一個3*3的小矩陣中的數字都不重複。
作者給兩種解法:
解法一:
下面的GenerateValidMatrix()函數用經典的深度優先搜索來生成一個可行解。從(0,0)開始,對沒有處理過的格子,調用GetValidValueList(coCurrent)來獲得當前格子可能的取值選擇,並從中取一個爲當前格子的取值,接着搜索下一個格子。在搜索uocheng中,若出現某個格子沒有可行的值,則回溯,修改前一個格子的取值,直到所有的格子都找到可行的取值爲止,這是可行解。
本人下面的代碼實現參考了http://blog.csdn.net/linyunzju/article/details/7673959博客,但我發現該博主的代碼實現有一處代碼順序的執行錯誤,那就運行其構造數獨的結果來看,發現最後一個格子竟然被賦值爲0,還有就是他的代碼實現只能固定的構造一個數獨,一般是前面的格子從小到大賦值,而不是隨機的去構造數獨,這樣不太符合構造不同的數獨,本人對此代碼做糾正和改進,有不恰之處,請多多包涵。
代碼實現:

#ifndef COOR_H
#define COOR_H
//存儲格子的行和列座標
class Coor
{
   public:
      Coor(int xvalue=0,int yvalue=0)
      {
         x=xvalue;
         y=yvalue;
      }
      int x;
      int y;
};
#endif

#ifndef CELL_H
#define CELL_H
#include<stdlib.h>
//代表每個格子的詳細信息
class Cell
{
   public:
      bool validList[10];//存儲格子賦值情況信息
      int validValue;//格子賦值
      Cell()
      {
         memset(validList,true,10*sizeof(validList[0]));
         validValue=0;
      }
      //判斷是否還存在有效值
      bool isNoValidValue()
      {
         for(int i=1;i<=10;i++)
         {
            if(validList[i]==true) return false;
         }
         return true;
      }
};
#endif // CELL_H

#include <iostream>
#include <cstdlib>
#include<Coor.h>
#include<Cell.h>
#define CLEAR(a) memset((a), 0, sizeof(a))
#define SIZE 9
Cell cell[SIZE+1][SIZE+1];//存儲每個格子詳細信息,索引代表格子的行和列座標
int value[SIZE+1];//索引代表對應格子的賦值,而該數組值代索引的數字值對當前格子的有效性
using namespace std;
int pickNextVailVale(int x,int y)
{
   CLEAR(value);
   //遍歷該已填的數字
   for(int i=1;i<y;i++)
   {
      value[cell[x][i].validValue]=1;
   }
   //遍歷該列已填的數字
   for(int i=1;i<x;i++)
   {
      value[cell[i][y].validValue]=1;
   }
   //求出是屬於哪一個九空格的第一個格子的行和列座標
   int ninegrid_x=(x-1)/3*3+1;
   int ninegrid_y=(y-1)/3*3+1;
   // 遍歷該九空格已填的數字
   for(int i=ninegrid_x;i<ninegrid_x+3;i++)
      for(int j=ninegrid_y;j<ninegrid_y+3;j++)
   {
      value[cell[i][j].validValue]=1;
   }
   //隨機生成數字
   int randomValue=(int)rand()%9+1;
   while(true)
   {
      //存儲已被用過的數字
      cell[x][y].validList[randomValue]=false;
      //如果該數字沒被用過,則返回該數字
      if(value[randomValue]==0)
      {
         return randomValue;
      }
      //如果所有數字都被用過,則回溯前一個格子
      if(cell[x][y].isNoValidValue())
      {
         return -1;
      }
      //尋找下一個數字
      randomValue++;
      randomValue=randomValue%9+1;
   }
}
//尋找下一個格子
void next(Coor &curCoor)
{
   curCoor.y++;
   if(curCoor.y>9)
   {
      curCoor.y=1;
      curCoor.x++;
   }
}
//回溯前一個格子
void pre(Coor &curCoor)
{
   curCoor.y--;
   if(curCoor.y<1)
   {
      curCoor.y=9;
      curCoor.x--;
   }
}
int main()
{
   Coor coCurrent(1,1);
   while(true)
   {
      //找不到滿足的情況
      if(coCurrent.x==0&&coCurrent.y==0)
         break;
      //附一個有效的數字
      cell[coCurrent.x][coCurrent.y].validValue=pickNextVailVale(coCurrent.x,coCurrent.y);
      //噹噹前格子沒有找到滿足的數字時,回溯前一個格子
      if(cell[coCurrent.x][coCurrent.y].validValue==-1)
      {
         //恢復初始化狀態
         cell[coCurrent.x][coCurrent.y].validValue=0;
         memset(cell[coCurrent.x][coCurrent.y].validList,true,10*sizeof(cell[coCurrent.x][coCurrent.y].validList[0]));
         pre(coCurrent);
      }
       else
        {
         // 滿足成功結果
            if (coCurrent.y==SIZE && coCurrent.x==SIZE)
            {
                for (int i=1; i<=SIZE; i++)
                {
                    for (int j=1; j<=SIZE; j++)
                        cout << cell[i][j].validValue << " ";
                    cout << endl;
                }
                break;
            }
            // 進一步搜索
            next(coCurrent);
        }
   }
    return 0;
}

解法二:
假設把整個棋盤分成9個九空格,按順序分別命名爲B1,B2,B3….B9.先從位於中心的九宮格隨機用1到9填滿該九宮格後,通過置換行的辦法把B4和B6矩陣填好,然後對中央小矩陣的每一列做同樣的變換,把B2和B8都解決了,再分別用B4和B6通過同樣的一些系列矩陣變換生成B1和B7以及B2和B8,代碼實現可以參考http://www.cnblogs.com/xulu/archive/2012/05/09/2491346.html

發佈了119 篇原創文章 · 獲贊 10 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章