约瑟夫环问题

来源:互联网 发布:英雄联盟17173数据库 编辑:程序博客网 时间:2024/05/12 20:44
假设问题是从n个人编号分别为0...n-1,取第k个,
则第k个人编号为k-1的淘汰,剩下的编号为  0,1,2,3...k-2,k,k+1,k+2...
此时因为从刚刚淘汰那个人的下一个开始数起,因此重新编号
把k号设置为0,则
0,1,2 ...n-k,n-k+1
n-k,n-k+1...0,1,2...
假设已经求得了n-1个人情况下的最终胜利者保存在f[n-1]中,则毫无疑问,该胜利者还原到原来的真正编号即为 (f[n-1]+k)%n (因为第二轮重新编号的时候,相当于把每个人的编号都减了k,因此重新+k即可恢复到原来编号)。由此,我们可以想象,当最终只剩下一个人的时候,该人即为胜利者,此时重新编号,因为只有一个人,所以此时f[1]=0
这样f[2]=(f[1]+k)%2,这样就可以求出最终胜利者在2个人的时候的情况下的编号,由递推公式f[n]=(f[n-1]+k)%n,可递推到最初编号序列中该胜利者的编号。
因此用这个方法,只需一遍On的扫描,即可求出最终答案

不过该题要求编号从1开始,只要把f[n]+1即可,同时,该题指定了第一个要删除的人必须为编号为m的人,其实也不难,求出f[n]之后,把原本编号为0的位置移到跟m只相距k的位置即可实现第一次删除的编号为m。所以最终 ans=(f[n]+1+m-k);

因为m-k可能为负数,导致整个ans为负,这样其实最后+n即可解决。

递归代码:

#include <stdio.h>  int Josephus(int n, int m) {      if(n == 1) {          return 0;      }      else {          return (Josephus(n-1, m) + m) % n;      }  }        int main() {      int n, m;      while (scanf("%d", &n) == 1) {          if (!n){              break;          }          scanf("%d", &m);          printf("%d\n", josephus(n, m)+1);      }      return 0;  }  





0 0
原创粉丝点击