hdu5643, 递归求解约瑟夫环问题

King‘s Game

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 832    Accepted Submission(s): 460

Problem Description

In order to remember history, King plans to play losephus problem in the parade gap.He calls n(1≤n≤5000) soldiers, counterclockwise in a circle, in label 1,2,3...n.
The first round, the first person with label 1 counts off, and the man who report number 1 is out.
The second round, the next person of the person who is out in the last round counts off, and the man who report number 2 is out.
The third round, the next person of the person who is out in the last round counts off, and the person who report number 3 is out.
The N - 1 round, the next person of the person who is out in the last round counts off, and the person who report number n?1 is out.
And the last man is survivor. Do you know the label of the survivor?

Input

The first line contains a number T(0<T≤5000), the number of the testcases.
For each test case, there are only one line, containing one integer n, representing the number of players.

Output

Output exactly T lines. For each test case, print the label of the survivor.

Sample Input

2

2

3

Sample Output

2

2

解题思路:

1、当不考虑时间开销的情况下可以用循环链表结构模拟游戏的进行过程, 建立N个节点的链表并初始化,每个节点中记录其一开始的序号,循环N-1次,每次找出要删除的节点并删除,最后剩下的节点中的值输出;

2、为了节省时间,采用效率比较高的递归算法,分析如下:

以下重新编号均是从删除的人员的后一位开始

第1轮中删除该轮中第1位人员,剩下的人员从1开始重新编号直到N-1作为第2轮的编号;

第2轮中删除该轮中编号为2的人员,剩下的人员从1开始重新编号直到N-2作为第3轮的编号;

第3轮中删除该轮中编号为3的人员,剩下的人员从1开始重新编号直到N-3作为第4轮的编号;

········

第 i  - 1 轮中编号为 t1 = (i - 1) % ( N - i  + 2),由于编号是从1 ~ N- i + 2,不存在编号为0的人员所以当算出来的t1= 0时,将(N - i + 2)赋值给t1,将该轮中编号为t1的人员删除,剩下人员编号从1开始编号直到N - i + 1作为第i轮的编号;

第 i 轮中编号为 t1 = i % ( N - i + 1),由于编号是从1 ~ N- i + 1,不存在编号为0的人员所以当算出来的t1= 0时,将(N - i + 1)赋值给t1,将该轮中编号为t1的人员删除,剩下人员编号从1开始编号直到N-i作为第i + 1轮的编号;

第 i  + 1轮中编号为 t1 = (i + 1) % ( N - i ),由于编号是从1 ~ N- i + ,不存在编号为0的人员所以当算出来的t1= 0时,将(N - i )赋值给t1,将该轮中编号为t1的人员删除,剩下人员编号从1开始编号直到N - i - 1作为第i +2轮的编号;

·········

第N轮中编号为t1 = N % (N - N + 1),由于t1 = 0,将(N- N + 1)赋值给t1

现我们倒着推,第1次推理,最后存活的人员在第N轮中的编号为1,F(1)= 1

第2次推理,最后存活的人员在第N - 1轮中的编号的计算 = (第N-1轮被淘汰人员的编号+ 在第N轮被淘汰人员在第N轮的编号) % i

直到第N次推理所得的F(N)即为最后淘汰者在第一轮中的编号


#include<iostream>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int N;
        cin >> N;
        int f = 1;
        for(int i = 2; i <= N; i++)
        {
            int t1 = (N - i + 1) % i;
            if(t1 == 0)
               t1 = i;
            f = (f + t1) % i;
            if(f == 0)
              f = i;
        }
        cout << f << endl;
    }
}

不考虑时间复杂度的情况下,该代码更好理解
#include<iostream>
#include<stdlib.h>
using namespace std;
typedef struct LinkNode
{
    int label;
    LinkNode *next;
}LinkNode;

int main()
{
    int T;
    cin >> T;//输入例子数目
        while(T--)
        {
            int N;
            cin>> N;//输入一共有几个人
            LinkNode *p, *q, *head;
            for(int i = 1; i <= N; i++)
            {//创建链表
                if(i == 1)
                {
                    head = (LinkNode *) malloc(sizeof(LinkNode));
                    p = q = head;
                    p->label = 1;
                    p->next = NULL;
                }
                else
                {
                    q = (LinkNode *) malloc(sizeof(LinkNode));
                    q->label = i;
                    q->next = NULL;
                    p->next = q;
                    p = q;
                    if(i == N)
                    {
                        p->next = head;
                    }
                }
            }
            for(int round = 1; round < N; round++)
            {//模拟淘汰游戏进行
                for(int i = 0; i < round; )
                {
                    p = q;
                    q = q->next ;
                    i++;
                    if(i == round)
                    {
                        p->next = q->next ;
                        free(q);
                        q = p;
                    }
                }
            }
            cout << q->label << endl;
        }
}

类似的可以参考http://blog.csdn.net/yanweibujian/article/details/50876631

时间: 2024-08-30 14:34:14

hdu5643, 递归求解约瑟夫环问题的相关文章

三种方法求解约瑟夫环问题

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列. 方法1:使用stl::list模拟环形链表,参考剑指offer 代码: #include <iostream> #include <list> using namespace std; int lastNumber(unsigned int n,un

51nod 1073约瑟夫环 递归公式法

约瑟夫环问题的原来描述为,设有编号为1,2,--,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,--,如此下去,直到所有人全部出圈为止.当任意给定n和m后,设计算法求n个人出圈的次序.  稍微简化一下. 问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 利用数学推导,如果能得出一个通式,就可以利用递归.循环等手段解决.下面给出推导的

bnuoj Musical Chairs 约瑟夫环非递归

/*问题描述:n个人(编号0~(n1-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n1-1个人组成了一个新的约瑟夫环(以编号为k=m%n1的人开始): k k+1 k+2 ... n1-2, n1-1, 0, 1, 2, ... k-2 并且从k开始报0. 现在我们把他们的编号做一下转换: n1=n+1; k --> 0-->0 k+1 --> 1-->1 k+2 --> 2

约瑟夫环的数学解法

CSDN链接 问题描述:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,直到圆桌周围的人全部出列.求最后剩下的人的初始编号. 可以把问题转换成:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号.则所得的解加1即为原问题的解: 一般我们采用一个循环队列来模拟约瑟夫环的求解过程,但是如果n比较大的时候,采用模拟的方

约瑟夫环问题,一道经典的数据结构题目

问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数.求胜利者的编号. 一般我们采用一个循环队列来模拟约瑟夫环的求解过程,但是如果n比较大的时候,采用模拟的方式求解,需要大量的时间来模拟退出的过程,而且由于需要占用大量的内存空间来模拟队列中的n个人,并不是一个很好的解法. 在大部分情况下,我们仅仅需要知道最后那个人的编号,而不是要来模拟一个这样的过程,在这种情况下,可以考虑是否存在着一种数学公式能够直接求出最后那个人的编号. 我们知道第一个人(编号

约瑟夫环问题小结

一 问题描述 约瑟夫环问题的基本描述如下:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为1的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,要求找到最后一个出列的人或者模拟这个过程. 二 问题解法 在解决这个问题之前,首先我们对人物进行虚拟编号,即相当于从0开始把人物重新进行编号,即用0,1,2,3,...n-1来表示人物的编号,最后返回的编号结果加上1,就是原问题的解(为什么这么做呢,下文有解释).而关于该问题的解

小朋友学数据结构(1):约瑟夫环的链表解法、数组解法和数学公式解法

约瑟夫环的链表解法.数组解法和数学公式解法 约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的,他参加并记录了公元66-70年犹太人反抗罗马的起义.约瑟夫作为一个将军,设法守住了裘达伯特城达47天之久,在城市沦陷之后,他和40名死硬的将士在附近的一个洞穴中避难.在那里,这些叛乱者表决说"要投降毋宁死".于是,约瑟夫建议每个人轮流杀死他旁边的人,而这个顺序是由抽签决定的.约瑟夫有预谋地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了他原先的牺牲品

约瑟夫环问题--递推解法

利用数学推导,如果能得出一个通式,就可以利用递归.循环等手段解决.下面给出推导的过程: (1)第一个被删除的数为 (m - 1) % n. (2)假设第二轮的开始数字为k,那么这n - 1个数构成的约瑟夫环为k, k + 1, k + 2, k +3, .....,k - 3, k - 2.做一个简单的映射. k         ----->  0              k+1    ------> 1              k+2    ------> 2           

约瑟夫环的java解决

总共3中解决方法,1.数学推导,2.使用ArrayList递归解决,3.使用首位相连的LinkedList解决 import java.util.ArrayList; /** * 约瑟夫环问题 * 需求:n个人围成一圈,从第一个人开始报数,数到K的人出局,然后从下一个人接着报数,直到最后一个人,求最后一个人的编号 * @author Miao * */public class Josephus { public static void main(String[] args) { int n =