约瑟夫环问题(递推)

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

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

0 0

输出
对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号
样例输入
6 2
12 4
8 3
0 0
样例输出
5
1
7
问题分析:
看到这个问题一打眼肯定是选择模拟整个过程来解题,但细心分析可以发现,这既然是一个数学问题,创立之初肯定不会是用计算机模拟的,肯定是有数学公式可以遵循的。所以我们可以从递推的角度来思考这个问题。
设n个人围城一圈报到m出列,最后的胜利者的编号设为f(n,m)。
首先我们先来看一个表格:

n m f(n,m)
1 2 1
2 2 1
3 2 3
4 2 1
5 2 3
6 2 5

这个表格呢,可以这么理解
n=6,m=2.最后胜利者为五号。
那我们来尝试推一下n=5,m=2时胜利者编号。当n=5时相当于n=6问题执行完一轮之后的子问题。n=6执行完一轮之后出局者是二号,那么就从三号开始报数,这时问题就变成了,五个人围成一圈报数,数到2的人退出,编号1为1、3、4、5、6这五个人(3号报数字1)。

编号 1 2 3 4 5 6
n=1 1 2 3 4 5 6
n=2 3 4 5 6 1
n=3 5 6 1 3
n=4 1 3 5
n=5 5 1
n=6 5

我标记黄色的数据是每一轮出局的人。n为当前为第n轮。
那么我们来思考接下啦一个问题。已知六个人围城一圈报到2出局,最后胜利者为5号,那么五个人围城一圈报到2出局,那么最后胜利者为几号?
解:这个问题不难理解,这就相当于六个人的胜利者少执行一轮报数,可知胜利者为5-2=3;三号就是胜利者。
那么我们仔细思考一下就可以把m看作周期T,每少执行一轮就将胜利者编号减去周期T,相反,每多执行一轮就将胜利者编号加上周期T。但是很明显这最后会出现不属于范围内的编号。所以我们就要对每一轮的结果f(n,m)取余。
取余的过程可以这么理解:f(n,m)-f(n,m)/n;当前编号包括多少个n,可知最后结果为每包括一个n编号就从1开始重置,所以取余结果为零的话编号就为1号,所以最后结果要加上一个1。
代码:

#include<iostream>
#include<cstdio>
using namespace std;
int f(int ,int);
int main()
{
    int n,m;
    cin>>n>>m;
    while(n!=0&&m!=0)
    {
        cout<<f(n,m)+1<<endl;
        cin>>n>>m;
    }
    return 0;
}
int f(int n,int m)
{
    if(n==1) return 0;
    else return (f(n-1,m)+m)%n;
}
发布了8 篇原创文章 · 获赞 7 · 访问量 388

猜你喜欢

转载自blog.csdn.net/amazingee/article/details/104061843