• 1748:约瑟夫问题


    总时间限制: 1000ms 内存限制: 65536kB
    描述
    约瑟夫问题:有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

    约瑟夫问题的递推公式是f[1]=0,f[i]=(f[i-1]+m)mod i。不过是一个“数据结构之指针和链表”里面的问题,所以还是先用链表和指针解决。因为要移除中间元素,所以需要一个双向链表,这里用一个数组来模拟:

    1、构建结构和数组:

    struct node{
        int id;
        node *pre;
        node *next;
    }lst[302];

    2、初始化数组元素的id:

        for(m=1;m<=302;m++){
            lst[m].id=m;
        }

    3、初始化数组元素的指针:

        for(i=1;i<=n;i++){
            lst[i].next=&lst[i+1];
            lst[i].pre=&lst[i-1];
        }
        lst[n].next=&lst[1];
        lst[1].pre=&lst[n];

    4、查询需要删除的元素,直到当前元素和下一个元素都指向同一个元素:

        node *cur=&lst[1];
        while(cur->next!=cur){
            for(i=2;i<=m;i++){            
                cur=cur->next;
            }
            cur->pre->next=cur->next;
            cur->next->pre=cur->pre;
            cur=cur->next;
        }

    此时,cur->id就是那个猴子王的初始编号。多组数据步骤2只需要进行一次,然后重复步骤3、4就可以了。但无论如何,这种方法的时间复杂度明显大于直接用数学方法进行求解,并且编码也多出很多。完整代码如下:

    #include<iostream>
    #include<cstring>
    using namespace std;
    struct node{
        int id;
        node *pre;
        node *next;
    }lst[302];
    
    int getking(int n,int m){
        int i;
        //1、设置前后指针
        for(i=1;i<=n;i++){
            lst[i].next=&lst[i+1];
            lst[i].pre=&lst[i-1];
        }
        lst[n].next=&lst[1];
        lst[1].pre=&lst[n];
        //2、查询
        node *cur=&lst[1];
        while(cur->next!=cur){
            //2.1、向后数到m
            for(i=2;i<=m;i++){            
                cur=cur->next;
            }
            //2.2、更新前后元素指针和当前数1的
            cur->pre->next=cur->next;
            cur->next->pre=cur->pre;
            cur=cur->next;
        }
        return cur->id;
    }
    int main(){
        int m,n;
        //初始化数组里的编号
        for(m=1;m<=302;m++){
            lst[m].id=m;
        }
        //获得输入并求解
        cin>>n>>m;
        while(m!=0 && n!=0){
            cout<<getking(n,m)<<endl;
            cin>>n>>m;
        }
    }

    然后,我们来考虑一下不模拟报数过程来求解,当一个人出局之后,剩下n-1人继续这个游戏,直到剩余1人。递推:1人时进行这个游戏,他不会出局的,两人呢?剩下的人是下次还能报数的那个,谁下次还能报数?同样,考虑3个人的情况,可以得出递推公式。核心代码如下:

        s=0;
    for(i=2;i<=n;i++){ s=(s+m)%i;
    } cout
    <<s+1;
  • 相关阅读:
    激光雷达slam之LOAM中的坐标转换与IMU融合
    记录一次失败的coding面
    因子图相关理论汇总
    [ICP]手推SVD方法
    SLAM中的卡方分布
    第五篇 openvslam建图与优化模块梳理
    第四篇 跟踪过程以及openvslam中的相关实现详解
    第三篇 视觉里程计(VO)的初始化过程以及openvslam中的相关实现详解
    第二篇 特征点匹配以及openvslam中的相关实现详解
    第一篇 特征提取以及openvslam中的相关实现详解
  • 原文地址:https://www.cnblogs.com/zcsor/p/6322579.html
Copyright © 2020-2023  润新知