约瑟夫问题
假设有问题如下:
步骤:
1.有牌面上写着从1到N的N张纸牌按照一定的顺序放在桌子上。
2.把此时最上面的一张牌放到纸牌的最下面去。
3.翻开此时最上面的一张牌,出现的是数字“1”。
4.把这张牌放到一边去。
5.把此时最上面的一张牌放到纸牌的最下面去。
6.翻开此时最上面的一张牌,出现的是数字“2”。
7.把这张牌放到一边去。
8.以此类推,每次都把最上面的牌放到最下面,再翻开最上面的一张牌。使得翻开的牌,是按照1到N的顺序出现。
问题:
在第一步当中,要如何排列这N张牌,可以做到这样的效果?
拿到这个问题,大家顿时傻眼了,不知如何下手。其实这就是约瑟夫问题~
逆向思维,假设我们已经知道初始时候牌的排列顺序,记录在数组D[]中,我们按照上述步骤以此翻牌,每个步骤对应的数据索引是已知的,而当前翻开的牌应该是多少也是固定要求的,因此当前索引对应的值就是固定的。因此,最终我们的数组D就确定了。
void calculate() { int Answer[N] = {0}; int numZero = 0; int index = -1; int count = 1; while (count <= N) { do { index = (index + 1) % N; // 环状循环 if (Answer[index] == 0) { // 发现了空位置0, numZero加1 numZero++; } if (numZero == 2) { // 发现了第2个空位置0,跳出循环 numZero = 0; break; } } while(1); Answer[index] = count; // 第2个空位置翻牌“自杀”。 count加1 count++; } }
值的注意的是,上述代码中出现了环状循环,大家需要熟悉它的用法。