C++,Migrated from Lutece 1937 猴子选大王

/*
Migrated from Lutece 1937 猴子选大王
Description
在m只猴子聚在一起选大王, 商定规则如下: 大家围成一圈, 按顺时针从1编号, 第一次从编号为1的开始报数, 
以后循环进行, 当报到n时退出圈子, 下一只则重新从1开始报数, 圈子中剩下的最后一只猴子则为大王.

Input
有多组测试数据. 输入的第一行是整数T(1<=T<=100), 表示随后测试数据的组数. 每组测试数据占一行, 由正整数m和n组成, 两数之间有一个空格. 1 <= m,n <= 200.

Output
对应每组测试数据, 输出选出的大王的猴子编号.
*/
#include <iostream>
#include <vector>
int main(){
    int T;std::cin>>T;
    while(T--){
        int m,n;std::cin>>m>>n;
        int start = 0;    
        std::vector<int> res;
        for(int i=1;i<=m;++i){
            res.push_back(i);
        }
        while(res.size()>1){
            start = (n-1 + start + res.size()) % res.size();
            res.erase(res.begin()+start);
        }
        std::cout<<res[0]<<std::endl;
    }
}


// xf的代码
// 约瑟夫环问题: 有n个人围成一圈,从第一个人开始报数,报到m的人出圈,下一个人继续从1开始报数,直到所有人出圈。问最后一个出圈的人是谁?

// 线段树(Segment Tree)是一种高级数据结构,主要用于处理区间查询和更新问题。
// 用于储存线性结构的数据,如数组、链表等,可以在对数时间内高效地进行区间查询和更新操作。
// 它可以在对数时间内高效地进行区间查询和更新操作。
// 线段树通常用于解决涉及区间的各种问题,例如区间求和、区间最小值、区间最大值等。 
// 线段树每个节点维护一个下标在[l,r]的区间, 根节点表示所有的数据,即[0,n-1]
// 叶子节点表示单个元素, 即只有一个数的区间[x,x], 其他节点的区间是其子节点区间的并集。
// 线段树的构建过程, 对一个区间为[l,r]的节点, 将该区间分为两半[l,mid]和[mid+1,r], 分别递归构建左右子树

// // 以下是一个简单的线段树实现,用于求区间和
// #include <iostream>
// #include <vector>
// class SegmentTree {
// private:
//     std::vector<int> tree;
//     //根节点编号为0
//     //tree中存储某节点下所有叶子节点的和
//     int n;

//     //构建线段树
//     void build(std::vector<int>& nums, int l, int r, int node) {
//         // nums为原始数组,
//         // l为当前区间左端点的下标,r为当前区间右端点的下标,根节点的区间为[0, n-1],n为原始数组的长度
//         // node为当前节点编号, 编号从0开始
//         if (l == r) {
//             //区间长度为1,说明是叶子节点, 表示单个元素
//             tree[node] = nums[l];//将原始数组的值赋给叶子节点
//             return;
//         }
//         //区间长度大于1, 将区间一分为二, 递归构建左右子树
//         int mid = (l + r) / 2;
//         //二叉树的线性存储结构(一个树从上到下,从左到右依次存储在数组中)
//         //若根节点编号从0开始,编号为node的节点的左子节点的编号是2*node+1,右子节点的编号是2*node+2
//         //将[l,mid]区间构建为编号为2*node+1的左子树
//         build(nums, l, mid, 2 * node + 1);
//         //将[mid+1,r]区间构建为编号为2*node+2的右子树
//         build(nums, mid + 1, r, 2 * node + 2);

//         //当前节点的值等于左右子树的和
//         tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
//     }
//     //查询区间和
//     int query(int l, int r, int ql, int qr, int node) {
//         //l,r为当前节点的区间, ql,qr为查询的区间
//         if (ql <= l && qr >= r) {
//             //如果当前节点区间内的所有元素都在查询区间内, 返沪所有元素的和(当前节点的值)
//             return tree[node];
//         }
//         if (ql > r || qr < l) {
//             //如果当前节点区间和查询区间没有交集, 返回0
//             return 0;
//         }
//         //当前节点区间和查询区间有交集, 将当前区间一分为二, 递归查询左右子树
//         int mid = (l + r) / 2;
//         //当前区间内在查询区间内元素的和等于左右子树内的元素分别在查询区间内的和的和
//         return query(l, mid, ql, qr, 2 * node + 1) + query(mid + 1, r, ql, qr, 2 * node + 2);
//     }
//     //更新线段树
//     void update(int l, int r, int idx, int val, int node) {
//         //l,r为当前节点的区间, idx为要更新的元素的下标, val为更新的值
//         if (l == r) {
//             //找到要更新的叶子节点, 更新叶子节点的值
//             //l==r==idx, 说明找到了要更新的叶子节点
//             tree[node] = val;
//             return;
//         }
//         //当前区间一分为二, 递归更新左右子树
//         int mid = (l + r) / 2;
//         if (idx <= mid) {
//             //要更新的元素在左子树中, 递归更新左子树
//             update(l, mid, idx, val, 2 * node + 1);
//         } else {
//             //要更新的元素在右子树中, 递归更新右子树
//             update(mid + 1, r, idx, val, 2 * node + 2);
//         }
//         //更新当前节点的值
//         tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
//     }

// public:
//     //构造函数
//     SegmentTree(std::vector<int>& nums) {
//         n = nums.size();
//         tree.resize(4 * n);
//         build(nums, 0, n - 1, 0);
//     }
//     //查询下标在[ql,qr]区间内元素的和
//     int query(int ql, int qr) {
//         return query(0, n - 1, ql, qr, 0);
//     }
//     //更新下标为idx的元素的值为val
//     void update(int idx, int val) {
//         update(0, n - 1, idx, val, 0);
//     }
// };

// int main() {
//     std::vector<int> nums = {1, 2, 3, 4, 5};
//     SegmentTree segTree(nums);
//     std::cout << "Sum of range [1, 3]: " << segTree.query(1, 3) << std::endl;
//     segTree.update(2, 10);
//     std::cout << "Sum of range [1, 3] after update: " << segTree.query(1, 3) << std::endl;
//     return 0;
// }


// xf的代码

// 线段树(Segment Tree)是一种高级数据结构,主要用于处理区间查询和更新问题。
// 它可以在对数时间内高效地进行区间查询和更新操作。
// 线段树通常用于解决涉及区间的各种问题,例如区间求和、区间最小值、区间最大值等。 
// 线段树每个节点维护一个范围为[l,r]的区间, 根节点表示所有的数据,
// 叶子节点表示单个元素, 即只有一个数的区间[x,x], 其他节点的区间是其子节点区间的并集。
// 线段树的构建过程, 对一个区间为[l,r]的节点, 将该区间分为两半[l,mid]和[mid+1,r], 分别递归构建左右子树

// #include <iostream>
// #include <vector>
// #include <algorithm>
// using namespace std;
// int sizea[1000000 * 4];
// int n, k;
 
// void buildtree(int l, int r, int cur) {
//     //cur表示当前节点在存储线段树的数组中的下标,下标从1开始
// 	if (l == r) {
// 		sizea[cur] = 1;//存1表示这个叶子节点有一个人
// 		return;
// 	}
// 	int mid = (l + r) >> 1;
// 	buildtree(l, mid, cur << 1);
//     //cur<<1,即cur*2,表示左子树的下标
// 	buildtree(mid + 1, r, cur << 1 | 1);
//     //cur<<1|1,即cur*2+1,表示右子树的下标
//     //左移1位后最后一位为0, 此时进行|1运算,将最后一位变为1,相当于+1

//     //sizea[cur]表示下标为cur的节点下的所有叶子节点的总人数
// 	sizea[cur] = sizea[cur << 1] + sizea[cur << 1 | 1];
// }

// //l,r表示当前节点包含的区间范围,要退出的人的为当前区间的第goal个人,cur表示当前节点的下标
// int quit(int l,int r,int goal, int cur) {
// 	sizea[cur]--;
//     //下标[l,r]的区间内还留着的人数-1
//     //保证在之后判断第goal个人时,会跳过以退出的人
// 	if (l == r ) {
// 		return l;
// 	}
// 	int mid = (l + r) >> 1;
//     //如果goal小于左子树中的人数, 说明要退出的人在左子树中
// 	if (goal <= sizea[cur<<1]) return quit(l,mid,goal, cur << 1);
//     //否则要退出的人在右子树中
// 	else return quit(mid+1,r,goal - sizea[cur<<1], cur << 1 | 1);
//     //函数返回退出的人的下标
// }

// void solve() {
//     //n个人, 每次报k个数
// 	cin >> n >> k;
//     //将下标为1到n的人构建成线段树
//     //根节点表示的区间为[1,n],根节点的下标为1
// 	buildtree(1, n, 1);

// 	int maxn = sizea[1];//根节点下的所有叶子节点的总人数
// 	int times = 0;
// 	int row = k;//报数的总数
// 	while (sizea[1] != 1) {
//         //要退出的人为剩余所有人中的第k个人
// 		k %= sizea[1];//若k大于当前区间的人数,则返回第一个人循环报数,即k=k%sizea[1]
// 		if (k == 0) k = sizea[1];//如果k==0,说明最后一个人退出,则k=sizea[1]
        
//         //要退出的人为剩余所有人中的第k个人
// 		quit(1,n,k, 1);
//         //第k个人已退出,下一个退出的人为第k个人之后的第row个人
//         //下一个退出的人为所有要退出的人中的第k+row-1,-1是因为第k个人已经退出
// 		times = 1;
// 		k += row - times;//row为每轮第几个人走
// 	}
// 	cout << quit(1,n,1, 1) << endl;
// }

// int main(){
//     int T;
//     std::cin >> T;
//     while(T--){
//         solve();
//     }
// }



posted @ 2025-03-12 19:12  Kazuma_124  阅读(51)  评论(0)    收藏  举报