题目摘要
时间限制: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;
}