预处理所有 92 个合法解并排序,随后 O(1) 回答每个查询。
OJ: roj
题目 ID: 1214
难度:普及
标签:搜索回溯位运算
创建: 2026-09-29 23:51
更新: 2026-10-05 05:42
题目描述
在
输入格式:第 1 行为
样例:输入 2 1 92,输出 15863724 和 84136275。
思路
8 皇后固定且只有 92 个合法解,先按行 DFS + 位运算剪枝全部枚举,按整数值排序后数组回答每次查询。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-29 23:51
# update_at: 2026-10-04 13:17
import sys
N = 8 # 棋盘为 N × N
ALL = (1 << N) - 1 # 低位 N 个 1,表示当前行可放的列掩码
def collect(solutions: list[str], row: int, cols: int, diag1: int, diag2: int, path: list[int]) -> None:
"""按行 DFS:row 为当前处理行,cols/diag1/diag2 分别记录已被占用的列、主对角线、副对角线。"""
if row == N:
# 列号转换为 1-indexed 后拼成整数串
solutions.append(''.join(str(c + 1) for c in path))
return
# 当前行仍可放的列:未被列或两条对角线攻击
free = ALL & ~(cols | diag1 | diag2)
while free:
low = free & -free # 取最低位 1 作为当前列
col = low.bit_length() - 1 # 列号 0..N-1
path.append(col)
# 下一行:列占用不变;对角线分别左移/右移一位表示攻击范围向下延伸
collect(solutions, row + 1, cols | low, (diag1 | low) << 1, (diag2 | low) >> 1, path)
path.pop()
free ^= low # 清除这一位,尝试下一列
def solve() -> None:
solutions: list[str] = []
collect(solutions, 0, 0, 0, 0, [])
solutions.sort() # 按整数字典序排列
data = iter(map(int, sys.stdin.buffer.read().split()))
q = next(data) # 询问组数
out: list[str] = []
for _ in range(q):
b = next(data) - 1 # 询问的序号(1-indexed)
out.append(solutions[b])
sys.stdout.write('\n'.join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 05:42
* update_at: 2026-10-05 05:42
*/
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 8; // 棋盘为 8 × 8
const int ALL = (1 << N) - 1; // 低 8 位为 1,表示当前行可放列的掩码
int col_of_row[N]; // col_of_row[i] 记录第 i 行皇后所在的列号(1..8)
vector<string> solutions; // 所有合法皇后串
// 按行 DFS:row 为当前处理行,cols/diag1/diag2 分别记录已被占用的列、主对角线、副对角线
void dfs(int row, int cols, int diag1, int diag2) {
if (row == N) {
string s;
for (int i = 0; i < N; ++i) s.push_back('0' + col_of_row[i]);
solutions.push_back(s);
return;
}
int free = ALL & ~(cols | diag1 | diag2); // 当前行仍可放的列
while (free) {
int low = free & -free; // 取最低位 1 作为当前列
int c = __builtin_ctz(low); // 列号 0..7
col_of_row[row] = c + 1;
// 下一行:列占用不变;对角线分别左移/右移一位表示攻击范围向下延伸
dfs(row + 1, cols | low, (diag1 | low) << 1, (diag2 | low) >> 1);
free ^= low; // 清除这一位,尝试下一列
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
dfs(0, 0, 0, 0);
sort(solutions.begin(), solutions.end()); // 按整数值排序
int n;
if (!(cin >> n)) return 0;
for (int i = 0; i < n; ++i) {
int b;
cin >> b;
cout << solutions[b - 1] << '\n';
}
return 0;
}