挺好滴

来源:互联网 发布:尔雅网络课程怎么刷课 编辑:程序博客网 时间:2024/04/29 21:31

所属题型:组合数学

要求知识:polya或者burnside

题意转述:一串由n个珠子组成的项链,用c种颜色涂染,问能形成多少种不同项链。

限制:旋转得来的为同一种,翻转得来的也为同一种。

本题有多种算法,下面给出比较容易想到且易于理解的两种算法。

推荐使用算法2,因为若是碰到高精度问题,算法2比算法1易于处理,且复杂度也比算法1低好多。核心知识点都是Polya定理。

解决过程:

算法一:

I.计算旋转置换和翻转置换的个数:

1.旋转置换

N个珠子,把项链当成一个圆,每次旋转角度为I*360/N(I∈(1,n)),共有N次不同旋转。

2.翻转置换

考虑对称轴:

n为奇数. 只有一种对称轴, 即轴穿过一个点. 这样有N种置换。

n为偶数, 有两种翻转:

轴每边n/2个点. 这样的置换有n/2个;

轴穿两点, 每边n/2个点. 这样的置换也有n/2个;

这样算出来结果还是有n个置换,即n为奇偶数是置换个数是一样的。

综上所述,共有2*N次不同置换,即有|G|=2N。

II.每个置换的循环节数

1.旋转置换

第i次旋转对应的置换结果:

[c-sharp] view plaincopyprint?
  1. for(j=0; j<n; j++)
  2. {
  3. g[j]=(i+j)%n;
  4. }
  5. 循环节数count由一下循环求出:
  6. for(j=0, count=0; j<n; j++)
  7. { //求每个旋转置换的循环节数,和寻找连通子图类似.
  8. if(flag[j]==0)
  9. { //若j结点未曾访问过,表示一个新的循环,count++.
  10. count++;
  11. for(k=j; flag[k]==0; k=g[k])
  12. flag[k]=1;
  13. }
  14. }

2.翻转置换

第i次翻转对应的置换结果:

[c-sharp] view plaincopyprint?
  1. for(j=1; (j+j)<n; j++)
  2. { //每次翻转置换的结果,沿翻转轴对称点交换位置.
  3. //由于程序里每次旋转置换在翻转置换之前进行,故每次只要用 //同一个翻转循环处理,而不必分情况讨论.
  4. k=g[j];
  5. g[j]=g[n-j];
  6. g[n-j]=k;
  7. }
  8. 循环节数count由以下循环求出:
  9. for(j=0, count=0; j<n; j++)
  10. { //求每个翻转置换的循环节数,和寻找连通子图类似.
  11. if(flag[j]==0)
  12. { //若j结点未曾访问过,表示一个新的循环,count++.
  13. count++;
  14. for(k=j; flag[k]==0; k=g[k])
  15. flag[k]=1;
  16. }
  17. }

算法二:

本题还可以快速算出每个置换的循环(或者轮换)个数,达到快速求解的目的:

分析(1)

1.旋转.

考虑顺时针旋转i格的置换:

循环个数为gcd(n,i)

每个循环的长度为L=n/gcd(n,i)

2.翻转

考虑对称轴

***n为奇数. 只有一种对称轴, 即轴穿过一个点. 有[n/2]个循环长度为2, 还有一个循环长度为1(被穿过的点), V=C([n/2], [m/2]).

***n为偶数, 有两种翻转

轴每边n/2个点. 这样的置换有n/2个

轴穿两点, 每边n/2个点. 这样的置换也有n/2个

算法复杂度分析:很容易可以算出这个算法的时间复杂度为O(n)。

分析(2):

Polya定理:设G是n个对象的一个置换群,用m种颜色涂染这n个对象,则不同染色的方案数L=1/|G|*[m^p(a1)+m^p(a2)+....+m^p(an)].其中p(ai)是某个置换的循环数.

1.旋转置换.

我们假设依次顺时针旋转1~n个,则循环个数=gcd(i,n);

2.翻转置换

当n为偶数时,分两种情况,一种是中心轴在两个对称对象上,则循环个数为n/2+1,另一种是对称轴两边分别有n/2个对象,则循环个数为n/2;

当n为奇数时,对称轴就只能在一个对象上,则循环个数为n/2+1;

[c-sharp] view plaincopyprint?
  1. void polya(int p,int n)
  2. {
  3. int i;
  4. ans = 0;
  5. for(i=0; i<n; i++)//第i次旋转的循环节数为gcd(n,i);
  6. ans += pow(p*1.0, gcd(n, i));
  7. if(n % 2) //n为奇数
  8. {
  9. for(i=0; i<n; i++)//共有n个循环节数均为 n/2+1 的置换;
  10. ans += pow(p*1.0, n/2+1);
  11. }
  12. else //n为偶数
  13. {
  14. for(i=0; i<n/2; i++) //如上面所述有两种置换,第一种循环节数均为n/2,
  15. ans += pow(p*1.0, n/2) + pow(p*1.0, n/2+1); //第二种循环节数均为n/2+1;
  16. }
  17. }

原创粉丝点击