总时间限制: 1000ms 内存限制: 65536kB

描述

约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。

输入

每行是用空格分开的两个整数,第一个是 n, 第二个是 m ( 0 < m,n <=300)。最后一行是:

0 0

输出

对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号

样例输入

  1. 6 2
  2. 12 4
  3. 8 3
  4. 0 0

样例输出

  1. 5
  2. 1
  3. 7

思路

一开始我是用一位数组直接模拟整个过程,样例输入输出是对的,但是超时了。

查阅资料后,得知本题可以由递推公式来得到猴王的编号,思路如下:

设由 monkeyNumber 个猴子,我们从0开始报数,报数3下就离开,即报到 3-1 = 2 的猴子离开。

  • 假设只有1只猴子,他就是猴王,他的编号是0
  • 假设有2只猴子,猴王编号是 (0 + 3) % 2 = 1
  • 假设有3只猴子,猴王编号是 (1 + 3) % 3 = 1
  • 假设有4只猴子,猴王编号是 (1 + 3) % 4 = 0
  • 假设有5只猴子,猴王编号是 (0 + 3) % 5 = 3
  • 假设有6只猴子,猴王编号是 (3 + 3) % 6 = 3
  • ……
  • 假设有monkeyNumber只猴子,猴王编号是(上一轮的猴王编号 + exitNum) % monkeyNumber.

所以递推公式如下:

POJ 2746 约瑟夫问题 - 图1%20%26%3D%20(f(monkeyNum%20-%201)%20%2B%20exitNum)%20%5C%25%5C%20monkeyNum%20%5C%5C%0Af(1%2C%20exitNum)%26%3D0%20%0A%5Cend%7Balign%7D%0A#card=math&code=%5Cbegin%7Balign%7D%0Af%28monkeyNum%2C%20exitNum%29%20%26%3D%20%28f%28monkeyNum%20-%201%29%20%2B%20exitNum%29%20%5C%25%5C%20monkeyNum%20%5C%5C%0Af%281%2C%20exitNum%29%26%3D0%20%0A%5Cend%7Balign%7D%0A)

代码

样例测试是对的,但超时的代码(数组直接硬刚果然效率不高)

  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. int array[301] = {0};
  5. int monkeyNumber, exitNum;
  6. int main() {
  7. while(true) {
  8. scanf("%d%d", &monkeyNumber, &exitNum);
  9. if(monkeyNumber == 0) return 0;
  10. int callNumber = 1, monkeyLeft = monkeyNumber;
  11. while(monkeyLeft != 1) {
  12. for(int i = 0; i < monkeyNumber; i++ ) {
  13. if(array[i] == 666) continue;
  14. if(callNumber == exitNum) {
  15. array[i] = 666; // This monkey exit
  16. callNumber = 1; // Reset the callNumber
  17. monkeyLeft--;
  18. continue;
  19. }
  20. callNumber++;
  21. }
  22. }
  23. for(int i = 0; i < monkeyNumber; i++) {
  24. if(array[i] != 666) {
  25. printf("%d\n", i + 1);
  26. memset(array, 0, sizeof(array)); // Reset the value of array
  27. break;
  28. }
  29. }
  30. }
  31. return 0;
  32. }

由数学递推公式得到的AC代码

  1. #include<iostream>
  2. using namespace std;
  3. int jonseOptimal(int monkeyNumber, int exitNum) {
  4. int final = 0;
  5. for(int total = 2; total <= monkeyNumber; total++) {
  6. final = (final + exitNum) % total; // total个猴子中winner为final.
  7. }
  8. return final + 1;
  9. }
  10. int main() {
  11. int monkeyNumber, exitNum;
  12. while(true) {
  13. scanf("%d%d", &monkeyNumber, &exitNum);
  14. if(monkeyNumber == 0) return 0;
  15. printf("%d\n", jonseOptimal(monkeyNumber, exitNum));
  16. }
  17. return 0;
  18. }