• 《算法竞赛进阶指南》数独


    数独是一种传统益智游戏,你需要把一个 9×99×9 的数独补充完整,使得数独中每行、每列、每个 3×33×3 的九宫格内数字 1∼91∼9 均恰好出现一次。

    请编写一个程序填写数独。

    输入格式

    输入包含多组测试用例。

    每个测试用例占一行,包含 8181 个字符,代表数独的 8181 个格内数据(顺序总体由上到下,同行由左到右)。

    每个字符都是一个数字(1−91−9)或一个 .(表示尚未填充)。

    您可以假设输入中的每个谜题都只有一个解决方案。

    文件结尾处为包含单词 end 的单行,表示输入结束。

    输出格式

    每个测试用例,输出一行数据,代表填充完全后的数独。

    输入样例:

    1. 4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
    2. ......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
    3. end

    输出样例:

    1. 417369825632158947958724316825437169791586432346912758289643571573291684164875293
    2. 416837529982465371735129468571298643293746185864351297647913852359682714128574936

    解题思路:

    1:选择一个有最少种的方案
    2:判断改方案是否合理,即同行/列/九宫格是否存在重复数
    3:利用二进制的表示法:1表示该位置能填数,0表示该位置不能填数:
    例如:1111 1111 0表示[2, 9]均可以填,而1不能填 

    1. #include
    2. #include
    3. using namespace std;
    4. const int N = 9, M = 1 << N;
    5. char str[100];
    6. int ones[M], map[M];//ones是计算一个正数表示为二进制数种1的数目,
    7. //map为将2的n次方转化为n:map[2的n次方] = n;
    8. int row[N], col[N], cell[3][3];//row, col, cell分别表示每行,每列,每个九宫格;
    9. void init()//将每行,每列,每个九宫格都填上1,即为每个格子都能填数
    10. {
    11. for (int i = 0; i < N; i ++ ) row[i] = (1 << N) - 1;
    12. for (int j = 0; j < N; j ++ ) col[j] = (1 << N) - 1;
    13. for (int i = 0; i < 3; i ++ )
    14. for (int j = 0; j < 3; j ++ )
    15. cell[i][j] = (1 << N) - 1;
    16. }
    17. void draw(int x, int y, int t, bool is_set)//在x, y位置上填1 << t;若is_set = true表示填数,
    18. { //反之删除一个数
    19. if (is_set) str[x * N + y] = t + '1';
    20. else str[x * N + y] = '.';
    21. int v = 1 << t;
    22. if (!is_set) v = -v;
    23. row[x] -= v;//改变第x行的数
    24. col[y] -= v;//改变第y行的数
    25. cell[x / 3][y / 3] -= v;//改变九宫格的数
    26. }
    27. int get(int x, int y)//返回第x行,第y列 ,此(x, y)所对应的九宫格中没有重复的数
    28. {
    29. return row[x] & col[y] & cell[x / 3][y / 3];
    30. }
    31. int lowbit(int x)//返回二进制中的最后一个1, 答案以2的次方表示例如:
    32. { //1000返回的是2的3次幂
    33. return x & - x;
    34. }
    35. bool dfs(int cnt)
    36. {
    37. if (!cnt) return true;//说明以及搜完
    38. int x, y;
    39. int minv = 10;//用来记录从哪个坐标开始枚举的选择最少
    40. for (int i = 0; i < N; i ++ )
    41. for (int j = 0; j < N; j ++ )
    42. if (str[i * N + j] == '.')//若该位置可以填数
    43. {
    44. int state = get(i, j);//找到该位置能填数的状态
    45. if (ones[state] < minv)//若该位置的最小选择可以被更新
    46. {
    47. x = i;
    48. y = j;
    49. minv = ones[state];
    50. }
    51. }
    52. int state = get(x, y);//说明x, y即为最小选择数目的方案,state即为最小选择数目的状态
    53. for (int i = state; i ; i -= lowbit(i))//每次除去末尾最后一个1
    54. {
    55. int t = map[lowbit(i)];//lowbit返回的是2的次幂,用map找出他的次幂
    56. draw(x, y, t, true);//在第x行,第y列,以及它所对应的九宫格上加上1 << t;
    57. if (dfs(cnt - 1)) return true;
    58. draw(x, y, t, false);//恢复现场
    59. }
    60. return false;
    61. }
    62. int main()
    63. {
    64. for (int i = 0; i < N; i ++ ) map[1 << i] = i;//预处理每个2的n次方所对应的次幂
    65. for (int i = 0; i < M; i ++ )//预处理每个i转化为二进制有多少个1;
    66. for (int j = 0; j < N; j ++ )
    67. ones[i] += i >> j & 1;
    68. while (cin >> str, str[0] != 'e')
    69. {
    70. init();//初始化九宫格
    71. int cnt = 0;
    72. for (int i = 0, k = 0; i < N; i ++ )
    73. for (int j = 0; j < N; j ++, k ++ )
    74. if (str[k] != '.')//说明能填数
    75. {
    76. int t = str[k] - '1';
    77. draw(i, j, t, true);//将(i, j)坐标上填上1 << t这个数
    78. }else cnt ++ ;//若不能填数,则所需要填的数++
    79. dfs(cnt);
    80. puts(str);
    81. }
    82. return 0;
    83. }

     

  • 相关阅读:
    Java SE 中的变量、数据类型和字符串类型以及类型转换和类型提升
    树状数组的两个操作的证明
    计算机视觉+人工智能面试笔试总结——模型量化
    什么是关系模型? 关系模型的基本概念
    前端基础:call、apply、bind的基本概念
    Nuxt3框架全局引用外部JS/CSS文件的相关配置方法
    vue 图片引入的各种方式
    【710. 黑名单中的随机数】
    丝滑的在RT-Smart用户态运行LVGL
    找不到d3dx9_37.dll,无法继续执行代码
  • 原文地址:https://blog.csdn.net/qq_61935738/article/details/125885105