输入n、m,总人数为n,每数到m杀一个人(这里设第一个序号为 t)
例:n=6, m=2
1, 2, 3, 4, 5, 6
1, , 3, 4, 5, 6
1, , 3, , 5, 6
1, , 3, , 5,
1, , , , 5,
, , , , 5,
设 f(n) 为第n个人死时,最后活着的人序号,当时有 1个人
设 f( i) 为第 i个人死时,最后活着的人序号,当时有 n-i+1个人
现有,
f(n) = 1
f(n-1) = [f(n)+m]%2
f( i) = [f(i+1) +m]%(n-i+1)
于是,从 f(n)一直推算出 f(1),需要执行 +m)%(n-i+1) 共 n-1 次
实现:
int t = 1;
for( int i = 2; i <= n; ++i)
{
t = (t+m)%i;
}
cout << t << endl;
注意,这里有一个错误,在 n-1次循环中,我们使用了求余运算,假设除数为i,则取值范围为[0, i-1]
而我们一直的计数是从1到n,而不是 0到n-1,如这还有3个人,我们的计数是 1,2,3,而不是 0,1,2
这就导致了当 t + m == i 时,我们原先的计数方式就使得 t = 0 而不可能取到 t = i,若单独拿出来判断浪费资源
加入判断,
t = (t+m)%i;
if( t == 0)
t = i;
故而,这里我们取从t=0开始计数的计数方式,计算过程不需要判断其它情况,若想得到原来计数方式的结果,+1即可
-------------------------------------------------------------------------------------------------------------------------------
更加明确的思路
约瑟夫环问题就是一群人闲得蛋疼坐在一起玩杀人游戏
编号,从1开始与从零开始的区别在于,从 1 开始就需要对序号是否为 0进行判断,若为零,则该名玩家的序号为当前玩家的人数,即序号为最后
举例,这里采用从 0 开始编号
现有 6人,n = 6
每数到 2,杀一个人,m = 2
游戏进行如下:
6人 => 杀一人=> 5人 => 杀一人=> 4人 => 杀一人=> 3人 => 杀一人=> 2人 => 杀一人=> 1人
我们需要求解的是,最后活下来的那个人,在游戏开始时的序号是多少
(约瑟夫想活下去)
设当有 i 名玩家活着时,约瑟夫的序号为 f(i),注意每杀一个人我们将死亡玩家的下一位玩家的序号设为 0
若约瑟夫最后活了下来,则 f(1) = 0
需要求解 f(6) 的值
问题的关键在于 f(i+1) 和 f(i) 的关系
0, 1, 2, 3, 4, 5
4, x, 0, 1, 2, 3
2, x, 3, x, 0, 1
0, x, 1, x, 2, x
1, x, x, x, 0, x
x, x, x, x, 0, x
如上,从 f(i+1) 到 f(i),约瑟夫(每个存活的玩家)的序号 -m)%i,即每杀一个人就 -m并取余当前存活人数 i 保持正值
则 f(i+1) -m = f(i),即 f(i+1) = ( f(i)+m ) % (i+1),因为反推,从 i 人反推到 i+1人状态,存活人数为 i+1
这里共杀了 n-1人剩下约瑟夫
int n = 6, m = 2;
int t = 0;
for(int i = 1; i <= n-1; ++i) // [1, 5]
{
t = (t+m)%(i+1);
}
693

被折叠的 条评论
为什么被折叠?



