汉诺塔问题是一个经典的递归算法案例,它展示了如何通过递归思维解决复杂问题。题目要求我们找出在移动n个盘子时的第m步操作。
汉诺塔问题的移动过程可以分为三个阶段:
每个阶段所需的步数遵循2^n-1的规律。对于n个盘子,总步数为2^n-1步。
cpp复制void hanoi_step(int n, int m, char from, char to, char aux) {
if (n == 1) {
cout << from << "--" << to << endl;
return;
}
int step = pow(2, n-1) - 1;
if (m <= step) {
hanoi_step(n-1, m, from, aux, to);
}
else if (m == step + 1) {
cout << from << "--" << to << endl;
}
else {
hanoi_step(n-1, m - (step + 1), aux, to, from);
}
}
提示:理解汉诺塔问题的关键在于把握递归三阶段的划分,以及步数计算方式。实际调试时可以打印中间变量观察递归过程。
这个问题要求将多个数字拼接成最大的整数,关键在于定义正确的比较规则。
cpp复制bool cmp(string a, string b) {
return a + b > b + a;
}
这个比较函数决定了两个字符串如何拼接才能得到更大的结果。例如:
cpp复制sort(list.begin(), list.end(), cmp);
string res;
for(int i=0; i<N; i++) {
res += list[i];
}
需要考虑全0的情况,此时应该输出单个0:
cpp复制if(res[0] == '0') {
cout << 0 << endl;
}
注意事项:比较函数的设计是本题的核心,必须确保拼接后的字符串比较而非数值比较。同时要注意前导零的特殊情况。
这个问题要求在n×n的棋盘上放置n个黑皇后和n个白皇后,且同色皇后不能互相攻击。
cpp复制// 黑皇后放置
void dfs_black(int row) {
if (row == n) {
// 重置白皇后标记
// 调用白皇后DFS
dfs_white(0);
return;
}
for (int col = 0; col < n; col++) {
if (board[row][col] == 1
&& !col_black[col]
&& !dg1_black[row - col + n - 1]
&& !dg2_black[row + col]) {
// 标记并递归
// 回溯取消标记
}
}
}
使用两个数组标记对角线:
调试技巧:可以打印中间状态观察皇后放置情况,特别是对角线标记是否正确。
这个问题在经典8皇后问题基础上增加了棋盘数字,要求找到数字和最大的放置方案。
cpp复制void dfs(int hang) {
if(hang == 8) {
if(cur_num > max_num) {
max_num = cur_num;
}
return;
}
for(int j=0; j<8; j++) {
if(!lie[j] && !zd[hang-j+7] && !fd[hang+j]) {
// 标记并累加数字
// 递归下一行
// 回溯取消标记
}
}
}
实际经验:这类问题中,回溯法的效率与剪枝策略密切相关。合理的剪枝可以大幅提高性能。
这个问题要求从多个芯片中识别出好芯片,已知好芯片比坏芯片多。
好芯片的测试结果具有一致性:
cpp复制for(int j=1; j<=n; j++) {
int num = 0;
for(int i=1; i<=n; i++) {
if(xp[i][j] == 1) num++;
}
if(num > n/2) {
cout << j << " ";
}
}
统计每列1的数量:
注意事项:这个解法利用了"好芯片占多数"的条件,使得统计结果可靠。如果好坏芯片数量相同,这种方法就不适用了。
在实际编码中,我经常使用打印中间状态的方法来调试递归和DFS算法。例如在汉诺塔问题中,可以打印每次递归调用的参数和当前阶段;在皇后问题中,可以打印当前的棋盘状态。这种方法虽然简单,但非常有效。