约瑟夫环问题

问题描述:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常,我们会要求输出最后一位出列的人的序号。

问题解法:https://blog.csdn.net/sinat_38972110/article/details/82150081

首先,数组写成以0开头更便于计算,所以后面的分析都是以0开头的。

1,借用模(取余),数组可以轻松表示环形。数组 [0, 1, 2, 3, 4, 5] 可以写成:[6%6, 7%6, 8%6, 3%6, 4%6, 5%6]. 我们把递增的起点从第0个元素移动到第3个元素处。即实现了3,4,5,0,1,2的单调递增。

2,把数组 [0,1,2,3,4,5,6,7,8,9] 滚动成 [4,5,6,7,8,9,0,1,2,3]的方法: 相当于是重新把6作为基准,其他数仍然照序递增(包括跨过数组右界回到数组左界,这个过程也是递增的),所以变化方法只需要给出6如何变成0即可: (k-6)%10 , 为了保证括号里大于零,所以额外加个模,即:(k-6+10)%10 ====> (k+4)%10.

 
解法一:用递归公式

每删除一个元素,就把新队列的下标映射成规范形式(即从0开始),这样已知最后一个取得元素其变换后的下标必然是m,则能逆向计算出变换前的下标。

   

int lastNum(int n, int m) {
        int index = 0, len = 1; //len表示变换前的数组长度
        while (++len <= n)
            index = (index + m % len + 1) % len;
        return index;
    }

解法二:用队列模拟

用队列表示环形数组,并且模拟数数的过程。数一个数相当于从队首取出一个元素,若计数没到k,就把它放回队尾,若到了k,就不再放回(相当于删除掉了),并且重置计数。直到队列剩余1个元素。

    #include<queue>
    int lastNum(int n,int m) {
        queue<int> q;
        for (int i = 0; i<n; i++)  q.push(i);    //初始队列0~n-1
        int count = 0;
        while (q.size() != 1) {    
            if (count != m) {    //未数到m        
                int tmp = q.front();
                q.pop();
                q.push(tmp);
                count++;
            }
            else {        //数到m        
                q.pop();
                count = 0;
            }
        }
        int res = q.front();
        return res;
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值