1. 程式人生 > 實用技巧 >LeetCode37題解(yield生成器提高速度)

LeetCode37題解(yield生成器提高速度)

37.解數獨

編寫一個程式,通過已填充的空格來解決數獨問題。
一個數獨的解法需遵循如下規則:

  1. 數字1-9在每一行只能出現一次。
  2. 數字1-9在每一列只能出現一次。
  3. 數字1-9在每一個以粗實線分隔的3x3宮內只能出現一次。

空白格用'.'表示。(左側為題目,右側為一個解答)

Note:

  • 給定的數獨序列只包含數字1-9和字元'.'
  • 你可以假設給定的數獨只有唯一解。
  • 給定數獨永遠是9x9形式的。

解題思路

9×9:看作3×3,然後每個單元又是一個3×3小塊
Solution:初始化小塊
big_permutations:大塊中挑選組合數最少的小塊,
    原因在於,一旦小塊確定下來,周邊的小塊的組合數會下降(由於限定條件增加),
    加快搜索速度,確定某個組合數最少的小塊,就把數字填入board,
    再從剩餘小塊篩選組合數最少的小塊,以此類推。
possible_blk:每個候選小塊(有些小塊已經確定,就不在候選範圍內)的可能組合,
    取其中組合數最少的返回,為了不浪費,生成n個生成器,不用馬上那個取回所有結果,
    而是先定義生成器,然後用next(生成器)來取,誰先取完,就是組合數最少的一個
small_permutations:小塊的組合 比如小塊中有4個空白, 每個空白可選值如下(由於塊,行,列已經填入數字的限制) [[
'4'], ['2', '7'], ['2', '7'], ['1']] 那麼可能的組合如下: ['4', '2', '7', '1'] ['4', '7', '2', '1']

程式碼

class Solution37(object):
    def solveSudoku(self, board):
        blk = [[board[b//3 * 3+ i//3][(b%3)*3+i%3] for
i in range(9)]for b in range(9)] blank_pos = [[i for i in range(9) if blk[k][i]=='.'] for k in range(9)] blank_val = [[str(i) for i in range(1,10) if not str(i) in blk[no]] for no in range(9)] for res in self.big_permutations(board,blank_pos,blank_val): return res
def big_permutations(self,board,blank_pos,blank_val,b_no=[]): no,pp = self.possible_blk(board,blank_pos,blank_val,b_no) for p3 in pp: for i in range(len(p3)): board[no//3*3 + blank_pos[no][i]//3][(no%3)*3 + blank_pos[no][i]%3] = p3[i] if len(b_no) == 8: yield board[:] else: for res in self.big_permutations(board,blank_pos,blank_val,b_no + [no]): yield res for i in range(len(p3)): board[no//3*3 + blank_pos[no][i]//3][(no%3)*3 + blank_pos[no][i]%3] = '.' def possible_blk(self,board,blank_pos,blank_val,b_no): pno_other = [i for i in range(9) if not i in b_no] p3 = [[] for i in range(9-len(b_no))] y3 = [] row = [[board[y][x] for x in range(9) if board[y][x]!='.'] for y in range(9)] col = [[board[y][x] for y in range(9) if board[y][x]!='.'] for x in range(9)] for k in range(len(p3)): no = pno_other[k] p = [0] * len(blank_pos[no]) for i in range(len(p)): y,x = no//3*3 + blank_pos[no][i]//3,(no%3)*3 + blank_pos[no][i]%3 p[i] = [m for m in blank_val[no] if not m in (row[y]+col[x])] y3.append(self.small_permutations(p)) while True: for k in range(len(p3)): try: res = next(y3[k]) p3[k].append(res) except StopIteration as e: return [pno_other[k],p3[k]] def small_permutations(self,p,per=[]): cur_floor = len(per) for pos in range(len(p[cur_floor])): t = per + [p[cur_floor][pos]] if len(set(t)) == len(t): if len(t) == len(p): yield [p[cur_floor][pos]] else: for result in self.small_permutations(p,per + [p[cur_floor][pos]]): yield [p[cur_floor][pos]] + result result = Solution37().solveSudoku([["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]) for r in result: print(r) print("-------------------------------")

在速度提高上下了功夫: