假设有问题如下:
步骤:
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++;
}
}
值的注意的是,上述代码中出现了环状循环,大家需要熟悉它的用法。