问题:有n个人站成环 从1开始报数,报k的人去死,之后下一个人报1,问当你是第几个的时候可以活下来?
这篇文章主要是讲解 f(n,k)=(f(n-1,k)+k)%n 这个公式是什么意思为什么是对的
虽然公式是使用数学解法 但开始时我会手动的模拟过程 其是有意义的 十分有助于理解
首先我们看样一个问题
n=2, k=3
a b
我们首先使用人力来数 a b a 很好 a死
接下来在试一遍 n=2 k=4
a b
人力:a b a b 很好b死
n=2 k=5
人力 a b a b a 很好a死
n=2 k=9999999
人力:T_T
对于 a b 数到k死 其实用k%2就可以很快的知道谁会死了
为什么?
a b
设k=9 ab一共有两个 所以 9%2=1 意思就是abab的数,数完最后一次完整的循环后又数了1个(数到了a) 这正是取模%的意义
现在我们还是用手动模拟 n=11 k=3
这次不用字母而是用数字编号以直接返回生存者的编号(剧透 注意分别 第几个和编号的不同)
1 2 3 4 5 6 7 8 9 10 11
第1个死去的 k%n 即 3%11=3 第三个死去 因为我们从死去的下一个开始重新数1 也就是说 死去的下个(编号4)是新的环的开始 死去的上个(编号3)是新的环的结束
4 5 6 7 8 9 10 11 1 2 (这就是新的环)
规则就是这样 下面我会直接写到生存者出现
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 1 | 2 | |
7 | 8 | 9 | 10 | 11 | 1 | 2 | 4 | 5 | ||
10 | 11 | 1 | 2 | 4 | 5 | 7 | 8 | |||
2 | 4 | 5 | 7 | 8 | 10 | 11 | ||||
7 | 8 | 10 | 11 | 2 | 4 | |||||
11 | 2 | 4 | 7 | 8 | ||||||
7 | 8 | 11 | 2 | |||||||
2 | 7 | 8 | ||||||||
2 | 7 | |||||||||
7 |
当当最后的幸存者是7
------完结撒花------
f(n,k)=(f(n-1,k)+k)%n这个公式到底描述的是什么呢?(怎么用?)
将上面表格的每一行看成数组 这个公式描述的是下一轮幸存者在这一轮的下标位置
f(n,k)求得值是 有n个人 数k死最后幸存的人的下标位置是几
这很明显是个递归的式子 需要从底到上的运算
最底端是 f(1,k) f(1,k)=0 就是说只有一个人的时候幸存者的下标是0(废话--) 编号是7
向上
f(2,k) =(f(1,k)+k)%n 在n=11 k=3是 f(2,3)=(f(1,3)+3)%2=3%2=1
在只剩两个人时 幸存者在这一轮数组中的下标位置是1 (看看上面的表格 下标位置为1 编号是7 )
向上
f(3,3)=(f(2,3)+3)%3=4%3=1
在只剩三个人时 幸存者在这一轮数组中的下标位置是1 (看看上面的表格 下标位置为1 编号是7 )
f(4,3)=(f(3,3)+3)%4=4%4=0
在只剩三个人时 幸存者在这一轮数组中的下标位置是0 (看看上面的表格 下标位置为0 编号是7 )
.
.
f(11,3)=(f(10,3)+3)%11=6%11=6 (看看上面的表格第一行 下标位置为6 编号是7 )
我们很容易可以根据式子写
1 int p=0;//结果 2 for(int i=2;i<=n;i++) 3 { 4 p=(p+k)%i; 5 }
p就是我们要求的第一轮的生存者的下标
而在第一轮中没有人死去(泪目..) 编号与下标的关系是编号=下标+1(数组从0开始存储)
所以完整的函数代码是
int cir(int n,int k) { int p=0; for(int i=2;i<=n;i++) { p=(p+k)%i; } return p+1; }
那么这个公式为什么是正确的呢?
当我们杀死一个一个人 并形成一个新环时 实质上是掉这个人并把整个环向前移动k位
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
第一轮去掉3
1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
们从死去的下一个开始重新数1 也就是说 死去的下个(编号4)是新的环的开始 死去的上个(编号3)是新的环的结束 就是把编号4前移k(3)格使他成为新环开始
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |||||||||
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 1 | 2 | 3 | ||||||
让我们在重申一遍f(n,k)=(f(n-1,k)+k)%n描述的是下标位置的变化
编号1的下标位置是0 0前移3格 下标位置变成-3 -3+11=8 所以在n轮下标位置为0的编号1在n-1轮中的下标位置变成了8
如果也有一个公式的话大概就是 f(n-1,k)=f(n,k)-k>=0?f(n,k)-k:f(n,k)-k+n;
那么从n-1轮的下标位置推算第n轮的下标位置就很清楚了 f(n,k)=f(n-1,k)+k>=n?f(n-1,k)+k%n:f(n-1,k)+k (%n 使得f(n,k)取得的下标在n的范围之内)
f(n,k)=(f(n-1,k)+k)%n
以上
重点在于f(n,k)描述的是下标位置的变化