约瑟夫问题

假设有问题如下:

步骤:
  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++;
    }
}

 

 

值的注意的是,上述代码中出现了环状循环,大家需要熟悉它的用法。


 

 

posted @ 2015-12-27 13:28  xfei.zhang  阅读(184)  评论(0编辑  收藏  举报