问题描述:皇帝决定找出全国中最幸运的一个人,于是从全国选拔出 n 个很幸运的人,让这 n 个人围着圆桌进餐,可是怎么选择出其中最幸运的一个人呢?皇帝决定:从其中一个人从 1 开始报数,按顺序数到第 k 个数的人自动出局,然后下一个人从 1 开始报数,数到 k 的人出局……。如此直到最后只剩下约瑟夫一人,然后他就成为全国最幸运的人。请问约瑟夫最初的位置?(注:原问题略显暴力,故自创此趣味题目)
分析:把第一个开始报 1 的人标定为 1,然后按报数顺序依次标定其余的人为:2,3,……,n - 1,n。按规则进行淘汰,直到最后剩一个数字,这个数字就是约瑟夫的位置。
解决方案:
1. 模拟法(simulation)
数组模拟,时间复杂度最高为 O(n3) (当k≈n时 ) ,空间复杂度O(n)
1 /********** 用数组模拟 *************/ 2 void findNext(bool *out, int n, int &curPosition){ 3 if(!out[curPosition]) { 4 int pNext = (curPosition + 1) % n; 5 if(!out[pNext]) 6 curPosition = pNext; 7 else{ 8 curPosition = pNext; 9 while(out[curPosition]) 10 curPosition = (curPosition + 1) % n; 11 } 12 }else 13 { 14 while(out[curPosition]) 15 curPosition = (curPosition + 1) % n; 16 } 17 } 18 int josephus(int n, int k) 19 { 20 if(n < 1 || k < 0) return -1; 21 if(n == 1) return n; 22 bool *out = new bool[n]; /********* 记录是否出局 *********/ 23 for(int i = 0; i < n; ++i) 24 out[i] = false; 25 int current = 0; 26 int n2 = n; 27 while(n2 != 1) 28 { 29 int cnt = k; 30 while(--cnt) 31 findNext(out, n, current); 32 out[current] = true; 33 findNext(out, n, current); 34 --n2; 35 } 36 delete[] out; 37 out = NULL; 38 return (current + 1); 39 }