63

【生活处处皆算法】巧用约瑟夫环,搭讪你心仪的妹子!

 5 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng%3D%3D&%3Bmid=2247485089&%3Bidx=2&%3Bsn=657d8ace76a7d7507d64b44d8150bf36&%3Butm_source=tuicool&%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

公众号后台回复“ 学习 ”,获取作者独家秘制学习资料

1 问题描述

约瑟夫环(约瑟夫问题)是一个数学的应用问题:

已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。

从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。

例如:有10个人围成一圈进行此游戏,每个人编号为 1-10 。若规定数到 3 的人出圈。则游戏过程如下。

(1)开始报数,第一个数到 3 的人为 3 号,3 号出圈。

1, 2, 【 3 】, 4, 5, 6, 7, 8, 9, 10。

(2)从4号重新从1开始计数,则接下来数到3的人为6号,6号出圈。

1, 2, 【 3 】, 4, 5, 【 6 】, 7, 8, 9, 10。

(3)从7号重新从1开始计数,则接下来数到3的人为9号,9号出圈。

1, 2, 【 3 】, 4, 5, 【 6 】, 7, 8, 【 9 】, 10。

(4)从10号重新从1开始计数,由于10个人称环形结构,则接下来数到3的人为2号,2号出圈。

1, 【 2 】, 【 3 】, 4, 5, 【 6 】, 7, 8, 【 9 】, 10。

(5)从4号重新从1开始计数,则接下来数到3的人为7号,7号出圈。

1, 【 2 】, 【 3 】, 4, 5, 【 6 】, 【 7 】, 8, 【 9 】, 10。

(6)从8号重新从1开始计数,则接下来数到3的人为1号,1号出圈。

1 】, 【 2 】, 【 3 】, 4, 5, 【 6 】, 【 7 】, 8, 【 9 】, 10。

(7)从4号重新从1开始计数,则接下来数到3的人为8号,8号出圈。

1 】, 【 2 】, 【 3 】, 4, 5, 【 6 】, 【 7 】, 【 8 】, 【 9 】, 10。

(8)从10号重新从1开始计数,则接下来数到3的人为5号,5号出圈。

1 】, 【 2 】, 【 3 】, 4, 【 5 】, 【 6 】, 【 7 】, 【 8 】, 【 9 】, 10。

(9)从10号重新从1开始计数,则接下来数到3的人为10号,10号出圈。

1 】, 【 2 】, 【 3 】, 4, 【 5 】, 【 6 】, 【 7 】, 【 8 】, 【 9 】, 【 10 】。

(10)最终剩余 4 号,4 号为胜利者。

2 数组求解

2.1 解题思想

用数组求解的基本思想就是用一个一维数组去标识这 n 个人的状态,默认全为 1 ,也就是都在圈子内。

当数到 m的人出圈之后,标识置为 0(就是出圈了),同时报数器清 0,下一个人要从 1 开始。

在每次报数之前要判断他是否在圈子内(也就是他的标识是否为 1 ),如果在圈子里面才会继续报数。定义一个变量记录出圈的人数, 出圈的人数等于  n-1 时,则游戏结束。

2.2 代码实现

#include
#define N 1000000 //记录玩游戏最大人数
int flag【N】 = {0};
int main()
{
int n = 0, m = 0;
scanf("%d%d", &n, &m);//输入玩游戏人数和计数m
int i = 0;
int count = 0; //记录已经出圈的人数
int num = 0; //报数器
for(i = 1; i <= n; i++) {
flag【i】 = 1;//所有人入圈
}
while(count < n - 1) {
for(i = 1; i <= n; i++ ) {
if (1 == flag【i】) {//在未出圈的人数中计数
num++;
if(num == m) {//此人数到m则出圈
printf("%d\n", i);
count++;//出圈人数加1
flag【i】 = 0;//出圈
num = 0;
}
if(count == n - 1) {
break;
}
}
}
}
for(i = 1; i <= n; i++) {
if(1 == flag【i】) {//输出赢家,标识为1
printf("The last one is : %d\n", i);
}
}
return 0;
}

3 循环链表求解

3.1 解题思想

约瑟夫环问题可以转化为循环链表的数据结构来求解。

可以将每个人看做链表的单个节点,每个节点之间通过链表的 next 指针连接起来,并且将链表末尾节点指向头节点就形成的环,由链表构成的环形结构在数据结构中称为循环链表。

3.2 代码实现

#include 
#include

/*声明一个链表节点*/
typedef struct node
{

int number;//数据域,存储编号数值
struct node *next;//指针域,指向下一个节点
}Node;

/*创建链表节点的函数*/
Node* CreatNode(int x)
{
Node *p;
p = (Node*)malloc(sizeof(Node));
p->number = x;//将链表节点的数据域赋值为编号
p->next = NULL;
return p;
}

/*创建环形链表,存放整数1到n*/
Node* CreatJoseph(int n)
{
Node *head,*p,*q;
int i;
for(i = 1; i <= n; i++)
{
p = CreatNode(i);//创建链表节点,并完成赋值
if(i == 1)//如果是头结点
head = p;
else//不是头结点,则指向下一个节点
q->next = p;
q = p;
}
q->next = head;//末尾节点指向头结点,构成循环链表
return head;
}

/*模拟运行约瑟夫环,每数到一个数,将它从环形链表中删除,并打印出来*/
void RunJoseph(int n,int m)
{
Node *p,*q;
p = CreatJoseph(n);//创建循环链表形式的约瑟夫环
int i;
while(p->next != p)//循环条件,当前链表数目大于1
{
for(i = 1; i < m-1; i++)//开始计数
{
p = p->next;
}
//第m个人出圈
q = p->next;
p->next = q->next;
p = p->next;
printf("%d--",q->number);//输出出圈的序号
free(q);
}
printf("\n最后剩下的数为:%d\n",p->number);
}

int main()
{
int n,m;
scanf("%d %d",&n,&m);
RunJoseph(n,m);
return 0;
}

4 递推公式求解

4.1 解题思想

约瑟夫环中,每当有一个人出圈,出圈的人的下一个人成为新的环的头,相当于把数组向前移动 m 位。

若已知 n-1 个人时,胜利者的下标位置位 f(n−1,m) ,则 n 个人的时候,就是往后移动 m 位,(因为有可能数组越界,超过的部分会被接到头上,所以还要模 n )

根据此推导过程得到的计算公式为:

f(n,m) = (f(n−1,m) + m) % n

其中,f(n,m) 表示 n 个人进行报数时,每报到 m 时杀掉那个人,最终的编号,f(n−1,m) 表示,n-1 个人报数,每报到 m 时杀掉那个人,最终胜利者的编号。有了递推公式后即可使用递归的方式实现。

4.2 递归代码实现

#include 
int Joseph(int n,int m)/*计算约瑟夫环的递归函数*/
{
if(n <= 1 || m <= 1)//设置游戏人数限定值
return -1;

if(n == 2)//设置边界值
{
if(m % 2 == 0)
return 1;
else
return 2;
}
else
{
return (Joseph(n-1,m) + m-1) % n+1;//递归调用
}
}

int main()
{
int n,m,x;
scanf("%d %d",&n,&m);
x=Joseph(n,m);
printf("最后一个数为:%d\n",x);
return 0;
}

4.3 迭代代码实现

#include 
/*计算约瑟夫环问题的迭代法函数*/
int Joseph(int n,int m)
{
int i;
int x,y;
if(n <= 1 || m <= 1)
return -1;
if(m % 2 == 0)
y = 1;
else
y = 2;

for(i = 3; i <= n; i++)
{
x = (y-1 + m) % i + 1;
y = x;
}
return y;
}

int main()
{
int n,m,x;
scanf("%d %d",&n,&m);
x = Joseph(n,m);
printf("最后一个的编号是:%d\n",x);
return 0;
}

划重点划重点

比如你们公司需要团建,你可以设计这个游戏,根据游戏人数的多少,将你和你心仪的妹子安排在合适的座位上,一同进最后的决赛圈~

公众号后台回复“ 学习 ”,获取作者独家秘制学习资料

喜欢本文的朋友,欢迎长按下图关注公众号 石杉的架构笔记 ,BAT架构经验倾囊相授

q2mmMfM.jpg!web

ZV3aIz7.gif


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK