题目
用红、蓝两种颜色将围成一圈的8个旗子染色。规定:若某两种染色方案通过旋转的方式可以重合,则只算一种。问:一共有多少种不同的染色方案?
问题分析
用0/1表示颜色方案,8个棋子对应8位二进制数。
”旋转重合则只算一种”即:将x循环左移以为得y,则x和y属于相同类别(等价类):
若y<x,则删除x;
若y>x,则删除y;
该算法借鉴求素数的筛法
由于每个方案只需要计算1次,时间复杂度为O(N)
棋子数目记为n,则N=2的n次方
除了全0和全1以外,所有偶数都是重复的,所有最高位为1都是重复的,根据这两个结论可以适当优化。
代码如下
// 循环左移一位
int RotateShiftLeft(int x, int N)
{
int hight = (x >> (N - 1));
x &= ((1<<(N - 1)) - 1);
x <<= 1;
x |= hight;
return x;
}
int Polya(int N, std::list<int>& answer)
{
int i, j;
int k1, k2;
int m = (1 << N);
int* p = new int[m]; // 记录每种方案
fill(p, p + m, 1);
for (i = 0; i < m; i++) // 遍历所有染色方案
{
for (p[i] == 1) // 尚未删掉
{
k1 = i;
for (j = 0; j <= N; j++)
{
k2 = RotateShiftLeft(k1, N);
if (k2 == i) // 说明完成了循环
{
break;
}
if (k2 > i) // 后面的k2无效
{
p[k2] = 0
}
else // if (k2 < i)
{
p[i] = 0; // i无效
break; // 前面必然便利过
}
k1 = k2;
}
}
}
for (i = 0; i < m; i++)
{
if (p[i] == 1)
answer.push_back(i);
}
delete[] p;
return (int)answer.size();
}