剑指offer 孩子们的游戏(圆圈中最后剩下的数)

1.题目

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

来源:剑指offer
链接:https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

2.我的题解

2.1 模拟法

  • 使用两重循环模拟数数的过程;
  • 外部循环代表出局小朋友的个数,共出局n-1个,留最后一个;
  • 内部循环代表从0数到m-1的过程,数到m-1,该小朋友出局;
  • 出局的小朋友被标记,数到该小朋友的时候直接跳过;
  • 最后还需要遍历一遍数组来找到最后留下的小朋友;
  • 空间复杂度:多次访问数组,数数的过程中更会有多次无效访问,估计是O(mn)
class Solution {
public:
	int LastRemaining_Solution(int n, int m)
	{
		if (n<1 || m<1)return -1;
		vector<int> isvisited(n, 0);
		int start = 0, cnt = 0;
		for (int i = 0; i<n - 1; i++) {//n-1 boys out
			int j = start;
			while (cnt<m) {//find the lucky boy
				if (!isvisited[j])
					cnt = cnt + 1;
				j = (j + 1) % n;
			}
            isvisited[(j-1+n)%n] = 1;
            start = j;cnt = 0;//next
		}
		for (int i = 0; i < n; i++)
			if (!isvisited[i])return i;
		return -1;
	}
};

3.别人的题解

3.1 公式推导法

  • 假设第一轮有n个人,数m个数,下标从0起;
  • 第一局出局的人是(m-1)%n
  • 第二局开始的人在第一局中位置为m%n
  • 第二局开始的人在第二局中位置为0
  • 第二局出局的人在第二局中位置为(m-1)%(n-1)
  • 第二局出局的人在第一局中位置为((m-1)%(n-1)+m)%m
  • 一般地,设dp[n]n人的情况下最后剩下的人(在n人圈里的序号),dp[n-1]n-1人的情况下最后剩下的人(在n-1人圈里的序号),有公式:dp[n]=(dp[n-1]+m)%n
  • dp[1]=0
  • 求解过程就是从最后一局(仅有一人)倒推的过程;

假设k=(m-1)%nn个人情况下第一局出局的人,且将第二局时小朋友重新编号,详细关系如下表

第一局结束时的编号(x)第二局开始时的重编号(xx)映射关系
k+1
k+2
……
k-2
k-1
k(已出局)
0
1
2
……
n-2
n-1(已出局)
xx=(x-k-1+n)%n
x=(xx+k+1)%n
k=(m-1)%n
x=(xx+m)%n
class Solution {
public:
	int LastRemaining_Solution(int n, int m)
	{
		if (n<1 || m<1)return -1;
		int res=0;
        for(int i=2;i<=n;i++)
            res=(res+m)%i;
		return res;
	}
};

3.2 链表法

  • 使用vector(或数组、链表(java)等有序、提供删除指定索引处元素函数的容器)模拟游戏过程,直到仅剩下一个 元素;
class Solution {
public:
	int LastRemaining_Solution(int n, int m)
	{
		if (n<1 || m<1)return -1;
		vector<int> vec;
		for (int i = 0; i < n; i++)vec.push_back(i);
		int start = 0;
		while (vec.size() > 1) {
			start = (start + m - 1) % vec.size();
			vec.erase(vec.begin()+start);
		}
		return vec[0];
	}
};

4.总结与反思

(1)约瑟夫环的站位;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值