约瑟夫环问题

本文介绍了约瑟夫环问题,并提供了两种解决方案:一种是通过模拟整个过程的传统方法,时间复杂度为O(n*k);另一种是利用递推方法,时间复杂度降低到O(n)。递推方法使用了一个简洁的递推公式来快速找到最后幸存者的编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递推方法解决约瑟夫环问题

这是一道蓝桥杯2018年国赛C/C++组的问题,题目是这样的:

n 个人的编号是 1~n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。
(报数是从1报起)当报到 k 的时候,这个人就退出游戏圈。下一个人重新从1开始报数。
求最后剩下的人的编号。这就是著名的约瑟夫环问题。

本题目就是已知 n,k 的情况下,求最后剩下的人的编号。

题目的输入是一行,2个空格分开的整数n, k
要求输出一个整数,表示最后剩下的人的编号。

约定:0 < n,k < 1百万

例如输入:
10 3

程序应该输出:
4

普通解法

模拟整个过程:建一个bool数组,true表示此人还活着,false表示已经自杀。这种解法的数据结构理应采用循环链表,但是对于这道题,只需要采用一般的数组,稍加一点循环链表的思想,即在控制i的增加的时候,把一般的i++改为i=(i+1)%n即可。
代码如下:

//约瑟夫环问题

#include <stdio.h>

int main()
{
	int n,k;
    scanf("%d %d",&n,&k);
    bool arr[n];
    for(int i=0;i<n;i++){
        arr[i]=true;
    }
    int now=0;
    int false_num=0;   //统计淘汰了几个人
    int j;
    if(n==1&&k==1){ //最简单的直接输出,下面就不用if了
        printf("1");
        return 0;
    }
    while(true){
        int num = 0;  //统计指针走过了几个true对象
        for(j=now;;j=(j+1)%n){
            if(arr[j]==true){
                num++;
                if(num==k){
                    arr[j]=false;
                    now = j+1;
                    false_num++;
                    num=0;
                }
                //printf("now = %d\nj = %d\nnum = %d\nfalse_num = %d\n",now,j,num,false_num);
            }

            if(false_num==n-1){
                for(int i=0;i<n;i++){
                    if(arr[i]==true){
                        printf("%d",i+1);
                        return 0;
                    }
                }
            }
        }
        
    }
	return 0;
}

这种方法时间复杂度为O(n*k)。

递推解法

F(1)=0

当有2个人的时候(N=2),报道(M-1)的人自杀,最后自杀的人是谁?应该是在只有一个人时,报数时得到的最后自杀的序号加上M,因为报到M-1的人已经自杀,只剩下2个人,另一个自杀者就是最后自杀者,用函数表示:

F(2)=F(1)+M

可以得到递推公式:

F(i)=F(i-1)+M

还是采用循环链表的思想

F(i)=(F(i-1)+M)%i

有了递推公式就可以在O(N)时间求出结果:

#include<stdio.h>

int main()
{
	int n;
	int k;
 
	scanf("%d %d",&n,&k);
	int result=0;  //N=1情况
	for (int i=2; i<=n; i++)
	{
		result=(result+k)%i;
	}
    printf("%d",result+1);
	return 0;
}


2020国赛加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值