定义
Kolakoski序列是一个仅由1和2组成的无限数列,是一种通过“自描述”来定义的数列。他的前几项为
1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1,…(OEIS上的A000002)
它的定义很简单,若把数列中相同的数定为一组,令a(1)=1,a(2)=2,则a(n)等于第n组数的长度。
可以根据这个定义来推算第三项以后的数:例如由于a(2)=2,因此第2组数的长度是2,因此a(3)=2,;
由于a(3)=2,所以第三组数的长度是2,因此a(4)=a(5)=1;由于a(4)=1,a(5)=1,所以第四组数和第五组数的长度都为1,因此a(6)=2,a(7)=1,以此类推。
说明
上面的构造方法或者说是定义首先要牢牢记住 a(n) 不仅代表第n位的值,还代表第n组的数字个数。
比如现在由序列(1, 2)生成Kolakoski序列。
已知a(1) = 1,所以第一组只有一个数,并且这个数已经是1了,那么下一组的数值应该去找序列的下一位,也就是2。
因此a(2) = 2,它又代表了第二组有两个数字,又根据a(2) = 2, 能够得到a(3) = 2。
又因a(3) = 2,它代表第三组有两个数字,上一组值是2,所以这一组值应该是1,所以a(4) = 1 a(5) = 1
又因a(4) = 1,代表第四组有一个数字,上一组使用的是1,所以这一组为2,因此a(6)=2
因a(5) = 1, 代表的是第五组有一个数字,上一组使用的是2,因此a(7) = 1
…
这里需要记住a(n)还代表第n组的数字个数。
这个序列有什么神奇的呢?对于(1,2)构造的序列,
1,2,2,1,1,2,1,2,2,1,2,2,1,1,2,1,1,2,2,1,2,1,1,2,1,2,2,1,1…
我们把相同数字进行合并得到
[1], [2,2], [1, 1], [2], [1], [2,2], [1], [2, 2], [1], [2, 2], [1,1]…
我们看每一组的数字个数,得到这样一个序列
1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 2…
不难看出它还是一个Kolakoski序列。
推广
那么有了Kolakoski的构造方法后,能不能举一反三,将其他序列也构造成Kolakoski序列呢?
当然可以了,比如序列(2, 1, 3, 1) 构造成Kolakoski序列是什么样呢?
已知a(1) = 2,因此a(1) = 2, a(2) = 2。
由上面知道了a(2) = 2,因此第二组有两个数字,而第一组使用了1,所以这一组用第二位的1. 既 a(3) = 1, a(4) = 1
a(3) = 1,所以a(5) = 3
a(4) = 1, 因此a(6) = 1
a(5) = 3, 因此a(7) = a(8) = a(9) = 2
… …
最后得到序列
[2, 2, 1, 1, 3, 1, 2, 2, 2, 1, 3, 3, 1, 1, 2, 2, 1, 3, 3, 3, 1, 1, 1, 2, 1, 3, 3, 1, 1,…]
例题
题目地址
题意
就是问你由(1,2)构造的Kolakoski序列中第n位值。
解题思路
预处理出题目范围的Kolakoski,然后直接输出即可。
AC代码
#include <iostream>
using namespace std;
int maxnum = 10000004;
int num[10000005];
void init()
{
num[1] = 1;
num[2] = 2;
num[3] = 2;
int t = 3; //t代表组num[t] 就是这一组有多少个数字
for (int i=4; i<=maxnum; i++)
{
int temp = num[t++];
if (temp == 2)
{
if (num[i-1] == 1)
{
num[i] = 2;
num[i+1] = 2;
i++;
}
else
{
num[i] = num[i+1] = 1;
i++;
}
}
else
{
if (num[i-1] == 1)
num[i] = 2;
else
num[i] = 1;
}
}
}
int main()
{
init();
int t, n;
cin >> t;
while (t--)
{
cin >> n;
cout << num[n] << endl;
}
return 0;
}