刷题(抄题)日记

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一.数独

1.题目描述

数独是根据9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含 1−9
且不重复。每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无解或多解的题目都是不合格的。

输入格式
一个未填的数独。

输出格式
填好的数独。
在这里插入图片描述

2.解答

(1)超时解法

直接使用dfs算法,从第一行第一列开始,如果有数字,就填充下一个,如果没有数字,就从1到9开始尝试,先判断是否合法,合法就填入数字,递归尝试下一个位置,下个位置失败的话会返回false,表明此次填充无法成立,就尝试下个数字。
上代码

// P1784 数独
#include <iostream>
using namespace std;

int arr[10][10];
bool yj(int r, int c) {
  if (r < 1 || r > 9 || c < 1 || c > 9) {
    cout << r << ' ' << c << endl;
    exit(0);
  }
  return false;
}
bool lg(int row, int col) {
  //   yj(row, col);
  //   cout << row << ' ' << col << endl;

  int cur = arr[row][col];
  for (int j = 1; j <= 9; j++) {
    // yj(row, j);
    if (arr[row][j] == cur && j != col) {
      return false;
    }
  }
  for (int i = 1; i <= 9; i++) {
    if (arr[i][col] == cur && i != row) {
      return false;
    }
  }

  int a = (row - 1) / 3 * 3 + 1;
  int b = (col - 1) / 3 * 3 + 1;
  for (int i = a; i <= a + 2; i++) {
    for (int j = b; j <= b + 2; j++) {
      //   yj(i, j);
      if (i != row && j != col && cur == arr[i][j]) {
        return false;
      }
    }
  }
  return true;
}
void print();
bool fill(int row, int col) {
  //   static int a = 0;
  //   a++;
  //   if (a == 20) {
  //     exit(0);
  //   }
  if (arr[row][col]) {
    if (row == 9 && col == 9) {
      return true;
    } else if (col == 9) {
      return fill(row + 1, 1);
    } else {
      return fill(row, col + 1);
    }
  } else {
    for (int i = 1; i <= 9; i++) {
      arr[row][col] = i;
      //   cout << endl;
      //   print();
      if (lg(row, col) == false) {
        arr[row][col] = 0;
        continue;
      }
      if (row == 9 && col == 9) {
        return true;
      } else if (col == 9) {
        bool ret = fill(row + 1, 1);
        if (ret) {
          return true;
        }
      } else {
        bool ret = fill(row, col + 1);
        if (ret) {
          return true;
        }
      }
    }
  }
  arr[row][col] = 0;
  return false;
}

void print() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cout << arr[i][j] << ' ';
    }
    cout << endl;
  }
}
int main() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cin >> arr[i][j];
    }
  }
  fill(1, 1);
  //   cout << endl;
  print();
  return 0;
}

代码很简单,就是递归尝试,但是会超时。数独有很多解法,但是这道题应该不会让我们用其他较复杂的解法,而是在细节处进行优化,这个优化是我看了题解才知道的。

(2)优化合法判断

很容易发现,如果不优化递归的次数,就只能从每次递归的操作量来优化,很明显,判断填充的数字是否满足行列要求和方格要求是很花时间的,特别是列和方格的,因为要跨行,所以可能会导致缓存不命中。

这里使用题解使用记录每行每列和每个方格的数字是否出现方式进行优化。

行列的好想,就是使用两个二维bool数组来记录数字是否出现: bool rows[10][10] bool cols[10][10]

方格的需要思考一下,但是也不难,就是找一个映射关系,让每个方格对映一个数,最好数字是连续的,方便使用数组时达到缓存命中这里使用的是 :**(row-1)/3*3 + (col-1)/3 **。映射不是唯一的,合理就行。

代码如下:

//数独
#include <iostream>
using namespace std;
int arr[10][10];
bool h[10][10], l[10][10], sq[11][11];
void print() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cout << arr[i][j] << ' ';
    }
    cout << endl;
  }
}
inline bool lg(int r, int c, int val) {
  return (!h[r][val]) && (!l[c][val]) &&
         (!sq[(r - 1) / 3 * 3 + (c - 1) / 3 + 1][val]);
}

inline void setval(int r, int c, int num, bool val) {
  arr[r][c] = num * val;
  h[r][num] = l[c][num] = sq[(r - 1) / 3 * 3 + (c - 1) / 3 + 1][num] = val;
}

bool dfs(int row, int col) {
  int nrow = col == 9 ? row + 1 : row;
  int ncol = col == 9 ? 1 : col + 1;
  if (arr[row][col]) {
    if (row == 9 && col == 9) {
      // print();
      return true;
    }
    return dfs(nrow, ncol);
  }

  for (int i = 1; i <= 9; i++) {
    if (lg(row, col, i)) {
      setval(row, col, i, true);
      if (row == 9 && col == 9) {
        return true;
      }
      bool ret = dfs(nrow, ncol);
      if (ret) {
        return true;
      }
      setval(row, col, i, false);
    }
  }
  return false;
}
int main() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cin >> arr[i][j];
      setval(i, j, arr[i][j], arr[i][j]);
    }
  }
  dfs(1, 1);
  // cout << endl;
  print();
  return 0;
}

(3)继续小优化

这里使用返回bool来回溯,但是没必要,到了结尾直接打印并退出就行
(当然,这不重要,没什么优化效果,反而可能因为引入了系统调用导致分支预判失败的代价变大【其实我也不清楚】)

//数独
#include <iostream>
using namespace std;
int arr[10][10];
bool h[10][10], l[10][10], sq[11][11];
void print() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cout << arr[i][j] << ' ';
    }
    cout << endl;
  }
}
inline bool lg(int r, int c, int val) {
  return (!h[r][val]) && (!l[c][val]) &&
         (!sq[(r - 1) / 3 * 3 + (c - 1) / 3 + 1][val]);
}

inline void setval(int r, int c, int num, bool val) {
  arr[r][c] = num * val;
  h[r][num] = l[c][num] = sq[(r - 1) / 3 * 3 + (c - 1) / 3 + 1][num] = val;
}

void dfs(int row, int col) {
  int nrow = col == 9 ? row + 1 : row;
  int ncol = col == 9 ? 1 : col + 1;
  if (arr[row][col]) {
    if (row == 9 && col == 9) {
      print();
      exit(0);
    }
    dfs(nrow, ncol);
    return;
  }

  for (int i = 1; i <= 9; i++) {
    if (lg(row, col, i)) {
      setval(row, col, i, true);
      if (row == 9 && col == 9) {
        print();
        exit(0);
      }
      dfs(nrow, ncol);
      setval(row, col, i, false);
    }
  }
}
int main() {
  for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
      cin >> arr[i][j];
      setval(i, j, arr[i][j], arr[i][j]);
    }
  }
  dfs(1, 1);
  cout << endl;
  print();
  return 0;
}

二.刷题之外的事情

本来还做了几道题(抄了几道题解),但是懒得打了,就说点其他的。

最近在准备华为ict比赛,但是感觉好难,有点想放弃,但是有感觉花了时间不想白费了,很犹豫,因为和我的其他安排冲突了。最近也没怎么看技术书籍了,代码就是刷了几道题(刷题好难),估计华为这个比赛结束后就会投入到期末考试的准备和项目准备的阶段。

对于项目,打算一月份之前把陈硕大佬的Muduo网络库给复现一边,把其中的一些代码改成现代c++(我是菜鸡,不敢说全部重构,只敢小改);如果顺利的话,就把cmu15445数据库刷了,感觉在上了c++实验课后,对现代c++的认识有了提升,之前看不懂的代码应该也能看懂了。寒假不知道能不能混个实习,能的话最好,不能的话就留校内卷,把xv6做了,下学期开学继续搓项目或者刷6824。

感觉学习好累,每天都有好多事情。最近开始练跑步了,虽然跑量不大,速度不快,但是对于从来不跑步,一跑就出人命的我来说还是有很大的进步了,希望能坚持下去,现在定了个目标,就是三公里跑到5:00就奖励自己一块运动手表,不知道需要多久,最好是期末之前能完成。

哎,本来还想去其他地方旅游的,但是真的没时间,可能最多就在武汉内旅游一下吧,这样会方便一点,应该会选择去灵泉寺,顺便去逛一下梁子湖。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值