有1到100共100個數, 從1開始, 每隔1, 2, 3... 個數拿走一個數, 最后剩下幾?(約瑟夫環)


最近找實習, 在做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     }

這樣感覺清爽多了. 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM