数独是一个我们都非常熟悉的经典游戏,运用计算机我们可以很快地解开数独难题,现在有一些简单的数独题目,请编写一个程序求解。
如有多解,输出一个解
import sys mat = [] for _ in range(9): mat.append([int(x) for x in input().split(" ")]) slots = [] s_i = [set(range(1, 10)) for _ in range(9)] s_j = [set(range(1, 10)) for _ in range(9)] s_c = [set(range(1, 10)) for _ in range(9)] for i in range(9): for j in range(9): if mat[i][j] == 0: slots.append((i, j)) else: s_i[i].remove(mat[i][j]) s_j[j].remove(mat[i][j]) c = (i // 3) * 3 + j // 3 s_c[c].remove(mat[i][j]) def aux(n): if n == -1: lines = [" ".join(map(str, row)) for row in mat] print("\n".join(lines)) return i, j = slots[n] c = (i // 3) * 3 + j // 3 for num in s_i[i].intersection(s_j[j]).intersection(s_c[c]): s_i[i].remove(num) s_j[j].remove(num) s_c[c].remove(num) mat[i][j] = num aux(n - 1) s_i[i].add(num) s_j[j].add(num) s_c[c].add(num) mat[i][j] = 0 aux(len(slots) - 1)
def findNext(board,row_ind,col_ind): for row in range(row_ind,9): for col in range(col_ind,9): if int(board[row][col])==0: return row,col for row in range(9): for col in range(9): if int(board[row][col])==0: return row,col return -1,-1 def check_valid(board,row_ind,col_ind,temp): for i in range(9): if temp == int(board[i][col_ind]): return False if temp == int(board[row_ind][i]): return False ini_row=(row_ind//3)*3 ini_col=(col_ind//3)*3 for row in range(3): for col in range(3): if int(board[row+ini_row][col+ini_col]) == temp: return False return True def solveSudoku(board,row_ind=0,col_ind=0): row_ind,col_ind=findNext(board,row_ind,col_ind) if row_ind == -1: return True for temp in range(1,10): if check_valid(board,row_ind,col_ind,temp): board[row_ind][col_ind]=str(temp) if solveSudoku(board,row_ind,col_ind): return True board[row_ind][col_ind]='0' return False try: while True: board=[] for i in range(9): board.append(input().split(' ')) if solveSudoku(board): for i in range(9): print(' '.join(board[i])) except: pass
def isOk(mat,i,j,num): #判断行中有无此数,有输出F if num in mat[i]: return False #判断列中有无此数,有输出F if num in [mat[x][j] for x in range(9)]: return False #判断所在格子里有无此数,有输出F ii,jj = i//3*3,j//3*3 piece = [] for k in range(3): for l in range(3): piece.append(mat[ii+k][jj+l]) if num in piece: return False #剩下的情况都合法,输出T return True def remain(mat,i,j): remain_list=[] for num in range(1,10): if isOk(mat,i,j,str(num)): remain_list.append(str(num)) remain_list.append('0') return remain_list def all_remain(mat): all_remain_list=[] for i in range(9): for j in range(9): if mat[i][j] == '0': remain_list = remain(mat,i,j) all_remain_list.append({'index':(i,j),'remain_list':remain_list,'remain_num':len(remain_list)}) all_remain_list=sorted(all_remain_list,key=lambda e:e.__getitem__('remain_num')) return all_remain_list def dfs(mat,all_remain_list,n): if n == len(all_remain_list): return mat else: (i,j) = all_remain_list[n]['index'] for num in all_remain_list[n]['remain_list']: if num == '0': return if isOk(mat,i,j,num): mat[i][j] = num result = dfs(mat,all_remain_list,n+1) if type(result) == type(None): mat[i][j] = '0' continue else: return result else: continue mat=[] for i in range(9): mat.append([x for x in input().split()]) mat = dfs(mat,all_remain(mat),0) for row in mat: print(' '.join(row))对之前的答案做了些改进,先填容易填的空,减少搜索时间
# coding: utf8 import sys sudoku_arr = [[]] queued = [[], [], [], [], [], [], [], [], []] candidate_value = [1, 2, 3, 4, 5, 6, 7, 8, 9] finded = False def set_queued(): col = 0 while col < 9: row = 0 while row < 9: if sudoku_arr[row][col] != 0: queued[col].append(sudoku_arr[row][col]) else: pass row += 1 col += 1 def back_trace(row, col): global finded global sudoku_arr global queued # 说明已经检测完了 if finded: return if row == 8 and col == 9: for line in sudoku_arr: print " ".join(str(i) for i in line) finded = True return # 该换行了 elif row < 8 and col == 9: row += 1 col = 0 # 如果row,col上有值,则跳过,直接测试下一个 if sudoku_arr[row][col] != 0: return back_trace(row, col + 1) # 进行测试 for i in candidate_value: # 如果本行,或者本列出现过i,则i不可取,不会对 # 全局结果产生任何影响,直接找下一个值,进行测试 if i in sudoku_arr[row]&nbs***bsp;i in queued[col]: continue row_tmp = (row / 3) * 3 error = False # 判断所属的小的9宫格内是否重复 while row_tmp < (row / 3) * 3 + 3: col_tmp = (col / 3) * 3 while col_tmp < (col / 3) * 3 + 3: if i == sudoku_arr[row_tmp][col_tmp]: error = True break col_tmp += 1 if error: break row_tmp += 1 if error: continue # 在row行和col列中都没出现过,则记录下来 # print "sudoku_arr[%d][%d]=%d" % (row, col, i) queued[col].append(i) sudoku_arr[row][col] = i back_trace(row, col + 1) sudoku_arr[row][col] = 0 queued[col].pop(-1) # for循环走完,说明没有适合该位置的值, # 也说明之前有错误的值,不必进一步试探下去,直接返回 return try: lines = 0 arrs = [] while True: line = raw_input() if line == "": break else: lines += 1 arrs.append([int(i) for i in line.split(" ")]) sudoku_arr = arrs if lines == 9: set_queued() back_trace(0, 0) lines = 0 break sys.exit(0) except Exception, e: sys.exit(1)由于一部分输入的解并非唯一解,因此代码得出的解和系统得出的不一定完全一致,因此,通过率不到100%。但代码是没问题的,可以通过对行求和,对列求和都等45进行验证
import sys def isOk(mat, i, j,num):#判断填入数字是否合法 for row in range(0,9):#遍历该列,若该数字已出现过,则不合法 if mat[row][j] == num: return False for col in range(0,9):#遍历该行,若该数字已出现过,则不合法 if mat[i][col] == num: return False ii = i//3 jj = j//3 for row in range(ii*3,ii*3+3):#遍历该位置所处的3*3矩阵,若该数字已出现过,则不合法 for col in range(jj*3,jj*3+3): if mat[row][col] == num: return False return True def dfs(mat,i,j):#深度优先遍历 if i==9:#所有行已遍历完,则结束 return mat if j==9:#所有列已遍历完,则进入到下一行 return dfs(mat,i+1,0) flag = False#flag表示该行有需要填充的格子 for col in range(j,9):#遍历该行的所有列,如果有值为0,则需要进行填充 if mat[i][col]==0: flag = True isChange =False#ischange表示是否已进行填充 for num in range(1,10): if isOk(mat,i,col,num):#找出1-9中能够合法填入的数字 isChange =True mat[i][col] = num tpp = dfs(mat,i,col+1)#将该位置填充后,该行的后续位置是否有解 if tpp == None:#如果后续位置无解,则将该位置重新置为0,未填充状态 isChange = False mat[i][col] = 0 continue#尝试下一个数字 else: return tpp if isChange==False:#找不到合法数字进行填充 return None if flag==False:#该行所有位置已填满,进入到下一行 return dfs(mat,i+1,0) while True: isCon = True mat = [] for i in range(9): line = sys.stdin.readline().strip() if not line: isCon = False break line =[int(i) for i in line.split(' ')] mat.append(line) if isCon ==False: break mat = dfs(mat,0,0) for line in mat: print(' '.join(str(j) for j in line))
import sys ALL_SET = {'1', '2', '3', '4', '5', '6', '7', '8', '9'} matrix = [] row_available = [] col_available = [] block_available = [[ALL_SET.copy() for _ in xrange(3)] for _ in xrange(3)] positions = [] # print block_available def sudoku(step=0): if step == len(positions): for i in xrange(9): print ' '.join(matrix[i]) print return pos_x, pos_y = positions[step] block_x, block_y = pos_x / 3, pos_y / 3 for num in row_available[pos_x].intersection(col_available[pos_y]).intersection( block_available[block_x][block_y]): matrix[pos_x][pos_y] = num row_available[pos_x].remove(num) col_available[pos_y].remove(num) block_available[block_x][block_y].remove(num) sudoku(step + 1) row_available[pos_x].add(num) col_available[pos_y].add(num) block_available[block_x][block_y].add(num) for _ in xrange(9): input_args = sys.stdin.readline() row = input_args.split() matrix.append(row) row_available.append(ALL_SET - set(row)) for i in xrange(9): col_available.append(ALL_SET - {matrix[n][i] for n in xrange(9)}) for i in xrange(9): for j in xrange(9): if matrix[i][j] == '0': positions.append((i, j)) else: block_x, block_y = i / 3, j / 3 block_available[block_x][block_y].remove(matrix[i][j]) sudoku(0)