题解 - 换位置游戏

题目描述

N 个小朋友(编号为 1 到 N)正在玩一个换位置游戏。从左到右依次排列着 N 个凳子 (编号为 1 到 N,最左边的为 1 号凳子,最右边的为 N 号凳子),每个凳子上都有一个数字 (凳脚处红色数字),每个数字互不相同,且都是不超过 N 的正整数。
游戏开始前,1 号小朋友坐在 1 号凳子上,2 号小朋友坐在 2 号凳子上,然后依次下去, N 号小朋友坐在 N 号凳子上。比如当 N=4 时,游戏开始前小朋友们坐凳子的状态如下图 1 所示:
在这里插入图片描述

图 1 游戏开始前 4 位小朋友坐凳子的状态
坐定后,游戏开始。每位小朋友看一下自己坐的凳子凳脚处的数字,然后根据这个数字 找到相应号码的凳子。比如上面的例子,1 号小朋友凳脚处数字是 3,所以他到 3 号凳子上 坐下,2 号小朋友凳脚处数字是 1,所以他到 1 号凳子坐下,3 号小朋友凳脚处数字是 2,所 以他到 2 号凳子坐下,4 号小朋友凳脚处数字是 4,所以他到 4 号凳子坐下。经过一轮换位 置以后,4 个小朋友坐凳子的状态如下图 2 所示:
在这里插入图片描述

图 2 经过第 1 轮换位置后小朋友们坐凳子的状态
坐定后,每位小朋友再看一下自己凳脚的数字,按照凳脚的数字再继续换位置,第二轮 换位置的结果如下图 3 所示:
在这里插入图片描述

图 3 经过第 2 轮换位置后小朋友们坐凳子的状态
坐定后,每位小朋友再看一下自己凳脚的数字,按照凳脚的数字再继续换位置,第三轮 换位置的结果如下图 4 所示:
在这里插入图片描述

图 4 经过第 3 轮换位置后小朋友们坐凳子的状态
当第三轮换位置结束后,发现每位小朋友又各自坐到了游戏开始前的位置上,此时游戏结束。
从上面的过程我们可以发现,从游戏开始经过 3 轮换位置后又回到了游戏开始前坐凳子 的状态,但当 N 很大的时候,这个换位置过程非常复杂,请编程帮忙计算一下最少需要经 过多少轮换位置才能回到游戏开始前坐凳子的状态。

输入
输入共 2 行。
第 1 行是一个整数 N(1≤N≤1000),表示参加游戏的小朋友人数,当然也表示凳子的 数目。
第 2 行 N 个互不相同的正整数 Ki(1≤Ki≤N,且 1≤i≤N),Ki 表示第 i 把凳子凳脚处的数字。
输出
输出共 1 行。
表示小朋友们通过换位置后回到游戏开始前坐凳子的状态最少需要经过多少轮。测试数据保证输出的结果不超出 20000000。
样例输入 Copy
3
1 2 3
样例输出 Copy
1
提示
输入有3把凳子。1号凳子凳脚处的数字为1,2号凳子凳脚处的数字为2,3号凳子凳脚处的数字为3。第1轮换位置后,1号小朋友仍然坐在1号凳子上,2号小朋友仍然坐在2号凳子上,3号小朋友仍然坐在3号凳子上。所以经过1轮就回到了游戏开始前状态。
对于60%的数据,1≤N≤500,且最少需要交换的轮数不超过10000。
对于100%的数据,1≤N≤1000,且最少需要交换的轮数不超过20000000。

题意

按照题目要求来换位置,求经过多少轮后所有的小朋友都回到了初始状态

分析

题目保证有解,所以每个小朋友的位置变化会有一个固定的周期,那么问题就转化成了求每个小朋友位置变化周期的最小公倍数。

对于每个小朋友,可以用递归来求解其周期。

// tar指该小朋友的初始状态的位置,now指当前位置,steps指经过了多少轮
int dfs(int tar,int now,int steps){
    if(tar == now) return steps;
    dfs(tar,k[now],steps + 1);
}

对于每个小朋友的周期,取最小公倍数

int res = 1;
for(int i = 1;i <= n;i++){
    int tmp = dfs(i,k[i],1);
    int gcd = __gcd(tmp,res); // 求最大公约数
    res = res / gcd * tmp; // 求最小公倍数,注意要先除再乘,防止溢出
}

代码

#include<bits/stdc++.h>
     
using namespace std;
  
const int N = 1000 + 10;
  
int n;
int k[N];
  
int dfs(int tar,int now,int steps){
    if(tar == now) return steps;
    dfs(tar,k[now],steps + 1);
}
  
int main(){
    ios::sync_with_stdio;
    cin.tie(0),cout.tie(0);
  
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> k[i];
  
    int res = 1;
    for(int i = 1;i <= n;i++){
        int tmp = dfs(i,k[i],1);
        int gcd = __gcd(tmp,res);
        res = res / gcd * tmp;
    }
  
    cout << res;
  
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值