牛客[编程题] HJ44 Sudoku数独游戏
困难 通过率:27.56% 时间限制:1秒 空间限制:32M
描述
问题描述:数独(Sudoku)是一款大众喜爱的数字逻辑游戏。玩家需要根据9X9盘面上的已知数字,推算出所有剩余空格的数字,并且满足每一行、每一列、每一个3X3粗线宫内的数字均含1-9,并且不重复。
例如:
输入
输出
数据范围:输入一个 9*9 的矩阵
输入描述:
包含已知数字的9X9盘面数组[空缺位以数字0表示]
输出描述:
完整的9X9盘面数组
示例1
输入:
0 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
0 4 5 2 7 6 8 3 1
输出:
5 9 2 4 8 1 7 6 3
4 1 3 7 6 2 9 8 5
8 6 7 3 5 9 4 1 2
6 2 4 1 9 5 3 7 8
7 5 9 8 4 3 1 2 6
1 3 8 6 2 7 5 9 4
2 7 1 5 3 8 6 4 9
3 8 6 9 1 4 2 5 7
9 4 5 2 7 6 8 3 1
个人感觉,这个题目出的不好,有多种答案,但是题目只给出一种答案;只要不符合标准答案,即使满足要求,也无法通过;6个用例只通过了5个;
后面的Check函数不是为了解出答案,而是为了检查输出的结果;
本方法还有改进的空间,目前无法唯一确定某一个值时,都是从可能的选项中默认选择第一个;
其实如果默认第一个的情况下无法解出答案时,可以用Check+Random的方式,也就是每试一次都是从可能的选项中随机选择一个,如果不行就再来一遍;
前提时要先把能唯一确定的最终状态记录下来,后面开始通过无限循环一直进行查找,总能找到答案;
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
string line; List<string> lines = new List<string>();
while ((line = System.Console.ReadLine()) != null)
{ // 注意 while 处理多个 case
lines.Add(line);
if (lines.Count == 9)
{
string[] lineArr;
int[,] arr = new int[9, 9];
int answer = 0; bool find;
for (int i = 0; i < 9; i++)
{
line = lines[i];
lineArr = line.Split(" ");
for (int j = 0; j < 9; j++)
{
arr[i, j] = int.Parse(lineArr[j]);
}
}
//最多循环81次
for (int k = 0; k < 81; k++)
{
if (k == 0)
{
find = true;
}
else
{
find = false;
}
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (arr[i, j] == 0)
{
answer = GetAnswer(i, j, arr, find);
if (answer != 0)
{
arr[i, j] = answer;
find = true;
}
}
}
}
//如果已完成,就立即结束循环
if (!HasZero(arr))
{
break;
}
}
//输出
for (int i = 0; i < 9; i++)
{
line = string.Empty;
for (int j = 0; j < 9; j++)
{
line += arr[i, j].ToString();
if (j != 8)
{
line += " ";
}
}
Console.WriteLine(line);
}
Check(arr);
}
}
}
//获取某个空位的答案
static int GetAnswer(int row, int column, int[,] arr, bool find)
{
//使用排除法确定可以填写的值
//一共1-9九个,如果排除到只剩一个,那么就可以确定答案了
List<int> left = new List<int>();
for (int i = 1; i <= 9; i++)
{
left.Add(i);
}
//从行开始排除
for (int i = 0; i < 9; i++)
{
if (i == column) continue;
left.Remove(arr[row, i]);
}
if (left.Count == 1)
{
return left[0];
}
//再从列排除
for (int i = 0; i < 9; i++)
{
if (i == row) continue;
left.Remove(arr[i, column]);
}
if (left.Count == 1)
{
return left[0];
}
//从所在的九宫格排除
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (row / 3 * 3 + i == row && column / 3 * 3 + j == column)
continue;
left.Remove(arr[row / 3 * 3 + i, column / 3 * 3 + j]);
}
}
if (left.Count == 1)
{
return left[0];
}
else if (!find && left.Count > 0)
{
return left[0];
}
return 0;
}
//判定矩阵中是否还有0,也就是判定是否已经完成
static bool HasZero(int[,] arr)
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (arr[i, j] == 0)
{
return true;
}
}
}
return false;
}
static bool Check(int[,] arr)
{
if (HasZero(arr)) return false;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
int answer = GetAnswer(i, j, arr, true);
if (answer != arr[i, j])
{
Console.WriteLine($"row={i},col={j},answer={answer},real={arr[i, j]}");
return false;
}
}
}
return true;
}
}