【题解提供者】吴立强
解法
思路
本题其实是改版的【约瑟夫环】问题,本质上还是依次找到某个位置的人,然后将这个人踢出队伍。
环状结构可以使用循环取模的方式来模拟(也就是代码中 p o s pos pos 的运行逻辑)。
踢出操作可以使用标记数组(也就是代码中的 v i s vis vis),false 代表没有被踢出,true 代表已经被踢出。
代码展示
#include <iostream>
using namespace std;
const int N = 109;
bool vis[N];
int a[N];
void solved() {
int n, m; cin >> n >> m;
/// 假设所有位置都没有被确定
for(int i = 0; i < n; i ++) vis[i] = false;
/// 以下两行用于标记第一个位置的牌是 1
a[0] = 1;
vis[0] = true;
/// 现在在第 pos 张牌的位置,已经把 sum 张牌放在牌堆底了
int pos = 0, sum = 0;
/// 寻找编号 2 至编号 n 的牌在原本牌堆中的位置
for(int i = 2; i <= n; i ++) {
/// 找到没有被使用的第 m+1 个位置,就是当前牌被安置的位置
while(sum <= m) {
/// 找到下一个位置
pos = (pos + 1) % n;
/// 计算当前是第几个未被使用位置(已使用不算位置)
sum += (vis[pos] == false);
}
/// 标记当前位置已使用、记录当前位置的牌编号、将 sum 置 0
vis[pos] = true;
a[pos] = i;
sum = 0;
}
for(int i = 0; i < n; i ++) cout << a[i] << ' ';
cout << endl;
}
int main() {
int t;
cin >> t;
while(t --) {
/// 分别完成 t 个子问题
solved();
}
return 0;
}
算法分析
本程序具体时间复杂度难以推出,但是可以确定其时间复杂度上限为 O ( t × n × n ) O(t \times n\times n) O(t×n×n)(因为 m ≤ n m\le n m≤n 所以单次查找位置最多扫描 n n n 个位置)。