hiho一下第167周数组重排

题目摘要

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi想知道,如果他每次都按照一种固定的顺序重排数组,那么最少经过几次重排之后数组会恢复初始的顺序?

具体来讲,给定一个1 - N 的排列 P,小Hi每次重排都是把第 i 个元素放到第 Pi个位置上。例如对于 P = (2, 3, 1),假设初始数组是(1, 2, 3),重排一次之后变为(3, 1, 2),重排两次之后变为(2, 3, 1),重排三次之后变回(1, 2, 3)。

被排数组中的元素可以认为是两两不同的。

输入
第一行一个整数 N ,代表数组的长度。 (1 ≤ N ≤ 100)

第二行N个整数,代表1 - N 的一个排列 P 。

输出
输出最少重排的次数。

样例输入
3
2 3 1
样例输出
3

题目分析

拿到这道题一开始想到的就是初始化一个[1,n]的int型数组a[n],和一个长度为n的排列数组P,然后再申请一个和a[n]长度相同的int型数组b[n],将a[n]中第i个位置上的数放到b[p[i]]上,最后判断b[n]数组是不是[1,n]升序即可。

#include"stdio.h"
#include"stdlib.h"

#define M 100

int main()
{
    int n, i, flag = 1, count = 0, p[M], a[M], b[M];
    scanf_s("%d", &n);
    //初始数组
    for (i = 0; i < n; i++)
    {
        a[i] = i + 1;
    }
    //排列P
    for (i = 0; i < n; i++)
    {
        scanf_s("%d", &p[i]);
    }
    while (flag==0 || count ==0)
    {
        //将第i个数放在Pi的位置上
        for (i = 0; i < n; i++)
        {
            b[p[i]-1]=a[i];
        }
        //更新数组a
        for (i = 0; i < n; i++)
        {
            a[i] = b[i];
        }
        count++;
        for (i = 0; i < n - 1; i++)
        {
            if (b[i + 1] - b[i] != 1)
            {
                flag = 0;
                break;
            }
            flag=1;
        }
    }
    printf("%d\n", count);
    system("pause");
    return 0;
}

但是测试只有90%AC,TIME LIMITED ERROR,于是又开始考虑另一种解法。
其实,每个位置都有对应的循环节,i位置上的数循环一定次数后一定会再次回到i,因此我们只要求出每个位置对应的循环节,再求出这些循环节的最小公倍数就是最后的解了。

#include"stdio.h"
#include"stdlib.h"

#define N 100

int gcd(int x, int y)
{
    return y ? gcd(y, x%y) : x;
}

int lcm(int x, int y)
{
    return x*y / gcd(x, y);
}
int main()
{
    int a[N],n,i,p,count,lc=1;
    scanf_s("%d", &n);
    for (i = 1; i <= n; i++)
    {
        scanf_s("%d", &a[i]);
    }
    //考虑每个位置的循环节
    for (i = 1; i <= n; i++)
    {
        if (i == a[i])continue;
        else
        {
            p = a[i];
            count = 1;
            while (p != i)
            {
                p = a[p];
                count++;
            }
            lc = lcm(lc, count);
        }
    }
    printf("%d\n", lc);
    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值