以前搞acm的时候,做的最少的就是数论这种题目了,可以说是完全不会,曾经接触过约瑟夫问题,如果作为一个模拟,给初学者做做挺好的,但是它居然有公式,而且公式和mod扯上了关系,乍一看以为又是和数论有关,其实,它最主要的是一个递归子问题,也是我比较喜欢的问题之一。
约瑟夫问题,有n个人,编号从0到n-1围成一个圈,从1开始,数m个,然后把第m个从这个圈中剔除,然后继续从1开始数m个,再剔除这个人,直到这个圈只有1个人,这个人就是胜利者。那么就是输入m和n,让你输出胜利者的编号。
初学者可以试试模拟,也能做,但是对于n和m比较大的情况,效率略低。
关于子问题:
假如现在编号为0到n-1,那我们找出来的第一个人编号是(m-1)%n,找出来之后,此时可以看做一个规模n-1的约瑟夫环,只是编号变了,编号变化如下:
(m-1)%n+1 0
(m-1)%n+2 1
(m-1)%n+3 2
............... ...
(m-1)%n-1 n-2
假如我知道右边一栏的问题(就是规模是n-1的问题)的答案是编号X,那么我只要把X对应到左边的编号,就是规模为n的问题的答案。那么我们需要一个将右边的数变成左边的数的方法,有:
左=(右+m)%当前的规模n
可以用如下方法推导:
f[i]表示在i个人情况下能过存活的人的编号,那么易得f[1]=0,
然后根据刚才的递归公式得到:
for(i=2;i<=n;i++)
f[i]=(f[i-1]+m)%i;
f[n]即为最后的结果
至此约瑟夫问题可以解决了。