本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
今天要写的题来自 Codeforces,是一道找规律的思维题。
Codeforces 1487B 猫循环:
题目大意: 你有两只猫,一只大猫 和一只小猫 ,和 个睡觉的位置, 和 在开始时分别位于 和 处,在此后的每个时刻两只猫都会按照以下规则进行移动:
- 从 开始每次向左移动 位,到达位置 后会返回 重新开始向左移动。
- 从 开始每次向右移动 位,到达位置 后会返回 重新开始向右移动。但是如果大猫 位于 , 小猫 B 位于 , 它们都要移动到 ,那么小猫 会把位置让给大猫,自己多移动 位,也就是 移动到 ,而 跳到 处。
第一行是样例个数 ,接下来是 行样例,每行都有一个表示位置个数的 和表示时刻的 ,你需要对每个样例都输出小猫 的位置。
二、思路分析:
这道题最朴素的做法是直接模拟,但是由于数据量很大,所以肯定会超时,所以我们需要观察一下移动的性质。
-
如果 为偶数,那么 和 永远不会发生碰撞。我们可以考虑最简单的 的情形,初始时 位于 , 位于 , 那下一步 就会移动到 , 而 则会移动到 , 后续的移动与此类似,不会发生冲突的可能。很容易就能得到:
-
如果 为奇数,直接模拟就复杂一些了,我们可以先试试看。令 , 初始位于 , 初始位于 ,第一次发生冲突前的时候 位于 ,而 位于 , 再移动一次后, 位于 , 而 位于 。这是第一次发生冲突的情况,而下一次发生冲突的情况很容易就能推出: 位于 ,而 位于 ,它们的下一步都是 , 再移动一次后 就到了 , 而 则位于 。通过比较可以发现: 从 移动到 所经过的位置是 个,而从 移动到 所经过的位置也是 个,也就是说从开始或者刚解决完冲突到再次发生冲突 需要走 步,而每次发生冲突都可以看作是 多走了一步,我们把多走的步数加上,就和第一种情况计算的方式相同了。归纳可得:
三、AC 代码:
#include <iostream>
using namespace std;
inline int calc(int n, int k) {
k %= n;
return k == 0 ? n : k;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n, k;
scanf("%d%d", &n, &k);
int ans;
if (n & 1) {
int m = n / 2;
k += (k - 1) / m;
}
ans = calc(n, k);
printf("%d\n", ans);
}
}
四、总结:
这种循环移动的题目考验的是找规律和归纳性质的能力,在平常的刷题过程中我们往往需要利用题目给出的性质化简操作,所以这种思维训练是必不可少的。最后,我们不妨改变题目中的一个条件,如果猫 在到达 后不是回到 ,而是从 往右移动,答案会是什么呢?如果 也不会回到 重新开始呢?感兴趣的同学可以思考一下。