变体N皇后问题C++实现:回溯/递归逻辑错误排查请求
Hey there! Let's work through your variant N-Queens problem. It sounds like you're hitting snags with the backtracking/recursion logic when the queen count doesn't match the board size—super common pitfall with this twist on the classic problem. Let's break down the most likely issues and how to fix them.
1. Wrong Base Case (The #1 Culprit)
The classic N-Queens stops when you've filled every row with a queen, but your variant needs to stop as soon as you've placed k queens total—regardless of whether you've reached the last row. A lot of folks accidentally carry over the classic base case, which breaks all k < n scenarios.
Wrong (Classic N-Queens):
if (row == n) { saveSolution(board); return; }
Correct (Variant):
if (queenCount == k) { saveSolution(board); return; } // Also add a guard for when we run out of rows early if (row >= n) return;
2. Forgetting the "Skip This Row" Option
In classic N-Queens, you have to place a queen in every row. But for k < n, you can choose to leave a row empty and move to the next one. This is the most overlooked part of the variant logic—without it, your code will only generate solutions where every row has a queen (i.e., k = n).
Add this after your loop of placing queens in the current row:
// Option to place NO queen in current row, proceed to next backtrack(row + 1, queenCount, board);
3. Broken Safety Check
Double-check your isSafe function to make sure it doesn't assume one queen per row. Since we can skip rows, the function only needs to check rows above the current one (since we process rows top to bottom):
Example Correct Safety Check:
bool isSafe(int row, int col, vector<vector<char>>& board, int n) { // Check same column above current row for (int i = 0; i < row; i++) { if (board[i][col] == 'Q') return false; } // Check upper-left diagonal for (int i = row-1, j = col-1; i >= 0 && j >= 0; i--, j--) { if (board[i][j] == 'Q') return false; } // Check upper-right diagonal for (int i = row-1, j = col+1; i >= 0 && j < n; i--, j++) { if (board[i][j] == 'Q') return false; } return true; }
4. Accidental Reference Passing for Queen Count
If you pass queenCount by reference instead of value, backtracking will mess up the count across branches. Always pass it by value so each recursive call gets its own copy:
// Good: queenCount is passed by value void backtrack(int row, int queenCount, vector<vector<char>>& board) { ... }
5. Solution Saving Errors
When saving a valid board state, make sure you copy the board into a new structure—don't store a reference. The board will be modified during backtracking, so references will overwrite your saved solutions:
void saveSolution(vector<vector<char>>& board, vector<vector<string>>& results) { vector<string> temp; for (auto& row : board) { temp.push_back(string(row.begin(), row.end())); } results.push_back(temp); }
Here's a stripped-down C++ framework you can compare against your code:
#include <vector> #include <string> using namespace std; class VariantNQueens { private: vector<vector<string>> results; int boardSize, queenCountTarget; bool isSafe(int row, int col, vector<vector<char>>& board) { // Same as the safe check above for (int i = 0; i < row; i++) if (board[i][col] == 'Q') return false; for (int i = row-1, j = col-1; i >=0 && j >=0; i--, j--) if (board[i][j] == 'Q') return false; for (int i = row-1, j = col+1; i >=0 && j < boardSize; i--, j++) if (board[i][j] == 'Q') return false; return true; } void backtrack(int currentRow, int queensPlaced, vector<vector<char>>& board) { if (queensPlaced == queenCountTarget) { vector<string> sol; for (auto& r : board) sol.push_back(string(r.begin(), r.end())); results.push_back(sol); return; } if (currentRow >= boardSize) return; // Try placing a queen in each column of current row for (int col = 0; col < boardSize; col++) { if (isSafe(currentRow, col, board)) { board[currentRow][col] = 'Q'; backtrack(currentRow + 1, queensPlaced + 1, board); board[currentRow][col] = '.'; // Backtrack } } // Try skipping this row entirely backtrack(currentRow + 1, queensPlaced, board); } public: vector<vector<string>> solve(int n, int k) { boardSize = n; queenCountTarget = k; results.clear(); vector<vector<char>> board(n, vector<char>(n, '.')); backtrack(0, 0, board); return results; } };
- Print intermediate states: Add
coutstatements in your backtrack function to log the current row, number of queens placed, and a snapshot of the board. This will show you exactly where the recursion is going off-track. - Test tiny cases first: Start with n=3, k=2. Manually list valid solutions (e.g., queens at (0,0) and (2,1)) and see if your code generates them.
- Isolate the "skip row" branch: Comment out that line and see if your code only returns k=n solutions—if yes, that branch is working as intended when uncommented.
内容的提问来源于stack exchange,提问作者kiril

