原题链接:http://poj.org/problem?id=2369
涉及群论置换相关知识,因为本人也是萌新,所以尽可能写得通俗易懂一点(可能不够严谨)。
主要知识有置换群循环节的性质,涉及到最小公倍数等一些基础数学知识。
用flag数组标注是否访问过该数字,无需对其进行操作。而对于未访问过的数字a,通过P寻找它的映射并用flag标注,循环直至重新访问到数字a,即该循环节访问结束,循环过程中记录循环节长度count。ans=lcm(ans,count)即可。所有数字都被访问过后得到的ans即结果。
先贴AC代码:
#include<stdio.h>
int p[1005];//题中p[n]
int flag[1005];//flag[i]标志是否访问过数字i,用来计算循环节
int gcd(int a,int b){//最大公约数
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){//利用最大公约数计算最小公倍数
return a*b/gcd(a,b);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&p[i]);
flag[i]=0;
}
//以上,输入,并给flag赋初值
int ans=1;//用来记录各循环节长度最小公倍数
for(int i=1;i<=n;i++)if(!flag[i]){//未访问过i才进入
flag[i]=1;//访问过i
int count=1,j=p[i];//count计算包含数字i的循环节长度,j表示该次访问的数字的映射
while(i!=j){//若循环节还未结束
count++;//访问过j,所以count++
flag[j]=1;//标记j
j=p[j];//更新j
}
ans=lcm(ans,count);//计算最小公倍数
}
printf("%d",ans);
return 0;
}
知识整理:
有限集合到自身的一一映射称为一个置换。即如题目中描述的,一般是指{1,2,3,…,n-1,n}→{1,2,3,…,n-1,n}的一个映射。题中n=5时,其中一个置换为 。
循环节,可以认为由置换中一组特殊的数字组成。如上,中1映射到4,4映射到2,2又映射到1,则认为构成一个循环,即认为124为一个循环节。在循环节中我们按题中所述的操作,对任意k,
构成的置换中,一定有{1,2,4}→{1,2,4}。而3,5也是一个循环节。
长度为L的循环节进行L次题中所述操作,即可达成循环节内,(i)=i。
可以这么认为,对于长度为k的循环节,循环节中的映射为
,记为
→
。一次操作后,即置换不变,
→
,
→
,……,
→
。循环节长度为k,所以其中
=
且
,
,…,
互不相同。进行两次操作(即
),
→
,依次类推,k次后
→
。故而k次操作有
→
,即满足
→
(i=1,2,…,k)。
若两循环节长度分别为x,y,设lcm(x,y)表示其最小公倍数,则经过lcm(x,y)次置换中这两个循环节满足:→
。
同样的,对于长度分别为x,y,z的循环节,经过lcm(x,y,z)次后满足→
。根据最小公倍数lcm(x,y,z)=lcm((x,y),z),那么只需要在寻找循环节过程中依次计算它们的最小公倍数。
对于最小公倍数,有lcm(a,b)*gcd(a,b)=a*b,gcd(a,b)为a,b最大公约数,那么可以用我们熟悉的gcd函数写出lcm函数。