最近找實習, 在做Test Assignment時遇到了這么道題, 就順便記錄下來:
說, 有1到100共100個數, 擺成一個圈. 從1開始, 每隔1, 2, 3, 4 ... 個數拿走一個數, 一直循環, 最后剩下幾? 具體的講就是一開始(隔0個數)把 1 拿走, 隔1個數(2)把3拿走, 再隔2個數(4, 5)把6拿走, 再隔3個數(7, 8, 9)把10拿走. 第一圈數到100之后接着從2開始數, 直到最后剩下1個數為止, 請問最后剩下幾? 如果是1到n呢?
1 public static int selectNumber(int n) { 2 3 int result = -1; 4 ArrayList<Integer> nums = new ArrayList<Integer>(); 5 int index = 0; // this variable is used to remove numbers from list 6 int count = 0; // count is used to count which numbers should be remove 7 int pIndex = 0; // this is used to record previous index 8 9 // generate a list contains numbers from 1 to n 10 for(int i = 1; i <= n; i++) { 11 nums.add(i); 12 } 13 14 while(nums.size() > 1) { 15 16 while(index < nums.size()) { 17 nums.remove(index); 18 count++; 19 pIndex = index; 20 index += count; 21 } 22 23 index = count - (nums.size() - pIndex); 24 25 while(index > nums.size() - 1) { 26 index = index - nums.size(); 27 } 28 } 29 30 result = nums.get(0); 31 return result; 32 } 33 34 public static void main(String[] args) { 35 int surviver = selectNumber(100); 36 System.out.println("The surviver is: " + surviver); 37 }
以上就是我的解決方案, 最后留下的數是31
2016.06.04更新:
在我發布這篇博文之后熱心網友指出這叫"約瑟夫環". 於是我就去網上搜了一下, 並找到了一個比較簡潔的約瑟夫環代碼(這個代碼不是我原創, 是在別人代碼的基礎上修改得來):
1 /** 2 * 約瑟夫環: 3 * n個數字擺成一個環, 從1開始數, 數到m或m的倍數的數字被刪除, 然后繼續, 直到剩下1個數字為止 4 */ 5 public static int josephProbBasic(int n, int m) { 6 ArrayList<Integer> list = new ArrayList<>(); 7 for(int i = 1; i <= n; i++) { 8 list.add(i); 9 } 10 // 用於計數 11 int count = 1; 12 // 當list中只剩下1個數時, 結束循環 13 for(int i = 0; list.size() > 1; i++) { 14 // 如果數到list的結尾, 從0開始重新數 15 if(i == list.size()) { 16 i = 0; 17 } 18 // 在m的倍數時, 刪除對應的數 19 if(count % m == 0) { 20 // 由於刪除之后, list中被刪除元素之后的元素都會依次向前移動一位, 因此也要把i向前移動一位 21 list.remove(i--); 22 } 23 count++; 24 } 25 return list.get(0); 26 }
但是這只是普通的約瑟夫環, 而我的問題要更麻煩一點. 於是乎, 我根據上面的思路重寫了這個問題的代碼:
1 /** 2 * 升級版約瑟夫環: 3 * n個數字擺成一個環, 從1開始數, 每隔m個數字刪除一個數字. m的初始值為0, 然后m++, 就是0, 1, 2, 3.... 4 * 這樣不斷刪除, 直到剩下1個數字為止. 5 * @param n 6 * @return 7 */ 8 public static int josephProbV2(int n) { 9 ArrayList<Integer> list = new ArrayList<>(); 10 for(int i = 1; i <= n; i++) { 11 list.add(i); 12 } 13 int count = 0; 14 int m = 0; 15 for(int i = 0; list.size() > 1; i++) { 16 if(i == list.size()) { 17 i = 0; 18 } 19 // 如果count數到m個數, 就刪除對應的數字, 同時count從零開始重新數, m = m + 1 20 if(count == m) { 21 list.remove(i--); 22 count = 0; 23 m++; 24 }else { 25 count++; 26 } 27 } 28 return list.get(0); 29 }
這樣感覺清爽多了.