1.
// 数独计数
// 小明最近热衷于数独游戏。在厌倦了传统数独后,他想到一个有趣的规则:有3x3的方格,
// 他要把1~9的这9个数字不重不漏地填入这9个方格,并且保证填入的每个数字周围没有临近数,
// 例如在中间位置填了数字4,那么数字3和5不能出现在数字4上下左右四个位置中任何一个。
// 现在小明已经填上了几个数字,他想知道一共有多少种符合上述规则的填充方案,
// 当且仅当至少存在一个位置在这两种方案中填充的数字不同时,两种填充方案被认为是不同的。
// 对每组数据,有三行,每行三个整数。
// 这三行三列中的第i行第j列的数aij代表题面描述中的3x3方格对应位置的情况。
// 如果aij=0表示那个格子还未填充;如果是一个[1,9]范围内的整数表示小明已填好的数字。
// 保证至少有一个格子未填充,已填充的格子不会出现重复数字,但有可能不合法。
// 回溯法
// 检查当前位置是否可以放置数字num
// 功能:检查在网格的(row, col)位置放置数字num是否合法。
// 实现:
// 检查当前位置的上下左右四个方向(如果存在)是否已经有数字与num的绝对值之差为1。
// 如果有,则返回false,表示不能放置;否则返回true。
// 示例:
// 如果num = 4,且左边是3或5,则返回false。
bool isValid(const vector<vector<int>>& grid, int row, int col, int num) {
// 检查上下左右四个方向
if (row > 0 && abs(grid[row - 1][col] - num) == 1) return false;
if (row < 2 && abs(grid[row + 1][col] - num) == 1) return false;
if (col > 0 && abs(grid[row][col - 1] - num) == 1) return false;
if (col < 2 && abs(grid[row][col + 1] - num) == 1) return false;
return true;
}
// 回溯函数
// 功能:递归地尝试填充网格,并统计所有合法的填充方案数。
// 参数:
// grid:3x3的网格,表示当前填充状态。
// row和col:当前需要填充的位置。
// used:一个布尔数组,记录数字1到9是否已经被使用。
// 实现:
// 终止条件:
// 如果row == 3,表示所有行都已经填满,找到一个合法方案,返回1。
// 列越界处理:
// 如果col == 3,表示当前行已经填满,递归进入下一行的第0列。
// 跳过已填充的位置:
// 如果grid[row][col] != 0,表示当前位置已经有数字,直接跳到下一个位置。
// 尝试填充数字:
// 遍历数字1到9,尝试将未使用且合法的数字num放在(row, col)位置。
// 如果num可以放置,则标记为已使用,并递归填充下一个位置。
// 递归返回后,撤销当前选择(回溯),继续尝试其他数字。
// 返回结果:
// 返回当前分支的合法方案数。
int backtrack(vector<vector<int>>& grid, int row, int col, vector<bool>& used) {
if (row == 3) {
// 所有位置都填满了,找到一个合法方案
return 1;
}
if (col == 3) {
// 当前行填满了,转到下一行
return backtrack(grid, row + 1, 0, used);
}
if (grid[row][col] != 0) {
// 当前位置已经填了数字,直接跳到下一个位置
return backtrack(grid, row, col + 1, used);
}
int count = 0;
for (int num = 1; num <= 9; ++num) {
if (!used[num] && isValid(grid, row, col, num)) {
// 尝试放置数字num
grid[row][col] = num;
used[num] = true;
count += backtrack(grid, row, col + 1, used);
// 回溯
grid[row][col] = 0;
used[num] = false;
}
}
return count;
}
2.// 小A爱魔法
// 小A一共会两种魔法,第一种魔法可以将纸上数字变为它的a倍,
// 比如在a=3时,他可以将666变成1998;
// 第二种魔法可以让纸上的数字循环右移一次,
// 比如12345在施加第二种魔法后会变成51234,119会变成911,
// 需要注意的是,小A无法对小于10或是以0为结尾的数施加第二种魔法。
// 纸上的数字初始是1,小A想将它变成自己的幸运数字b,那么请问小A至少需要施加几次魔法。
// 如果无论如何都无法将它变成b,请输出-1。
解题: bfs
int magic(int a, int b) {
// bfs 查找
queue<pair<int, int>> q; // 存储当前数字与步数
q.push({ 1,0 });
unordered_set<int> visited; // 记录已经访问过的数字
visited.insert(1);
while (!q.empty()) {
auto current = q.front();
q.pop();
int num = current.first;
int steps = current.second;
if (num == b) {
return steps; // 找到目标数字
}
// 尝试第一种魔法:乘以a
int nextNum1 = magic_1(num, a);
if (nextNum1 < 1e6 && visited.find(nextNum1) == visited.end()) {
visited.insert(nextNum1);
q.push({ nextNum1, steps + 1 });
}
// 尝试第二种魔法:循环右移
int nextNum2 = magic_2(num);
if (nextNum2 != -1 && visited.find(nextNum2) == visited.end()) {
visited.insert(nextNum2);
q.push({ nextNum2, steps + 1 });
}
}
return -1; // 无法到达目标数字
}
int magic_1(int& num, int a) {
return a * num;
}
int magic_2(int num) {
if (num < 10 || num % 10 == 0) {
return num;
}
int lastDigit = num % 10;
int remaining = num / 10;
int digits = 0;
int temp = remaining;
while (temp != 0) {
temp /= 10;
digits++;
}
int result = lastDigit;
for (int i = 0; i < digits; ++i) {
result *= 10;
}
result += remaining;
return result;
}