0%

约瑟夫环

摘要

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

最后一条毛毛虫

题目描述

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

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

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

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

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

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

输入

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

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

输出

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

输入样例

1
5 3

输出样例

1
4

样例解释

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

题目来源

北航OJ

分析

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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]都是成立的。那么我们就可以写递归代码了,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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,甚至不需要数组来存,直接就能出来,代码如下:

1
2
3
4
5
6
7
8
9
10
11
#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;
}

结语

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