约瑟夫环问题 例题解决

摘要

主要是之前一直不知道约瑟夫环的dp解法,这里和大家分享一下。本文用三种方法给大家呈现本题。

最后一条毛毛虫

题目描述

小D的花园里养了 n 条毛毛虫,每条毛毛虫都有自己独有的编号(从 1 到 n )。

一开始,所有的毛毛虫都开开心心地生活在花园里,享受着食物和阳光。

直到某一天,额……它们知道自己肯定会被吃掉,所以就开始玩一个游戏,来决定被吃的顺序。游戏的方法是这样的:

所有毛毛虫按照编号顺序从 11 到 n 顺时针围成一个环,然后从 1 号毛毛虫开始顺时针依次报数,报到数字 m 的毛毛虫就出环被吃,下一条毛毛虫继续从 1 开始顺时针报数……直到所有毛毛虫都出环被吃。

那么,最后一条被吃的毛毛虫的编号是多少呢?

时间限制:1000ms,内存限制:65536KB

输入

两个正整数 n,m ,意义如题所示。

输入数据保证: 1≤n,m≤3000。

输出

一个正整数,代表最后一条被吃的毛毛虫的编号。

输入样例

5 3

输出样例

4

样例解释

毛毛虫被吃的顺序依次为:3、1、5、2、4 。

题目来源

北航OJ

分析

(1) 首先很显然最简单的约瑟夫环问题,肯定用最容易理解的方法就是循环链表了。通过指针遍历、删除来完成每次的报数和出局环节,最后只剩一条毛毛虫时,就结束就可以了。虽然非常容易理解,但是代码比较长,时间花费也比较高(和后面介绍的方法相比),但本题的限制还是可以随便AC的。代码如下:

#include<stdio.h>
#include<stdlib.h>
struct node{
	int num;
	struct node *next;
};
struct node *p,*r,*head;
void insert(int n,int m){
	int i;
	head=NULL;
	for(i=1;i<=n;i++){
        r=(struct node*)malloc(sizeof(struct node));
        r->num=i;
        if (head==NULL)
            head=p=r;
        else{
            p->next=r;          
            p=p->next;
        }
    }
    p->next=head;
    r=p;
    p=head;
   	while(p->next!=p){
         for(i=0;i<m-1;i++){
               r=p;
               p=p->next;
          }
          r->next=p->next;
          free(p);
          p=r->next;
      }
      printf("%d", p->num);
}

int main(){
	int m,n;
	scanf("%d%d",&n,&m);
	insert(n,m);
}

(2) 第二第三种方法本质一样,只是实现方式有所区别。都是通过数学方法进行一些推导得到的。首先我们假设编号从0开始,最后求出的答案再加1就好了,所以可以知道在第一次中,肯定是叫到(m-1)%n的会出局,那么第一次出局的下一个肯定就是m%n了,如果把他当作0号,那剩下的n-1个人又组成了一个新的约瑟夫环,此时出局的就是(m-1)%(n-1),但是因为现在的0号并不是0号,而是m%n号,所以我们得到的胜利者还要加回去,也就是((m-1)%(n-1)+m%n)%n,如果我们把第n-1次胜利者看作f(n-1)的话,那第n次的胜利者肯定就是(f(n-1)+m%n)%n,那对于第三次开始叫号也就是同样的道理,胜利者编号就为((f(n-2)+m%(n-1))%(n-1)+m%n)%n,可以看出是有规律的,我们用式子写出来就是f(n)=(f(n-1)+m%n)%n,f(n-1)=(f(n-2)+m%(n-1))%(n-1),化简得到就一个式子f(n)=(f(n-1)+m)%n,对于n∈[0,n]都是成立的。那么我们就可以写递归代码了,如下:

#include<stdio.h>
int m;
int func(int n)
{
    if(n==1)
    {
        return 0;
	}
    return (func(n-1)+m)%n;
}
int main()
{
    int n;
    scanf("%d%d",&n,&m);
    printf("%d",func(n) + 1);
    return 0;
}

(3) 但是这样递归,其实多了很多重复计算的步骤,是可以优化的,即DP,所以转移方程也就是上文提到的f(n)=(f(n-1)+m)%n,甚至不需要数组来存,直接就能出来,代码如下:

#include<stdio.h>
int main()
{
    int n,m,s=0,i;
    scanf("%d%d",&n,&m);
    for(i=2;i<=n;i++){
    	s=(s+m)%i;
	}
	printf("%d",s+1);
    return 0;
}

结语

有谬误敬请指出,敬请不吝赐教



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值