约瑟夫游戏的大意:30 个游客同乘一条船,因为严重超载,加上风
浪大作,危险万分。因此船长告诉乘客,只有将全船一半的旅客投入
海中,其余人才能幸免于难。无奈,大家只得同意这种办法,并议定
30 个人围成一圈,由第一个人数起,依次报数,数到第 9 人,便把
他投入大海中,然后再从他的下一个人数起,数到第 9 人,再将他投
入大海中,如此循环地进行,直到剩下 15 个游客为止。问:哪些位
置是将被扔下大海的位置?
(1)不失一般性,将 30 改为一个任意输入的正整数 n,而报数上限
(原为 9)也为一个任选的正整数 k。
(2)要求使用顺序存储结构和链式存储结构分别实现。
吐槽:
很多题有很多方法,就一种方法又会有很多种表达形式,许多语法细节使用还可以不一样…
有时想把一些经典问题的做法好好归纳下,却又常常有种无从下手的感觉。
这里依次列了些方法,感觉整体的结构有些杂乱ㄟ( ▔, ▔ )ㄏ
推荐大家只看代码1就好了
1.直观想法:模拟
#include <iostream>
#include <cstring>
using namespace std;
int main() //约瑟夫环问题"模拟"做法
{
int n,k,i=0; //总人数 报数上限 当前角色对应编号
cin>>n>>k;
int a[n],sum=n,num=0; //模拟数组 圈内人数 计数器
memset(a,0,sizeof(a)); //0表示在环中
while(sum>15) //总人数大于15游戏继续,这里15可以根据情况不同调节
{
i=i%n; //i是从0开始的,i到了n就归零
if(!a[i]) num++; //当前角色在圈中,则计数++
if(num==k) //当前角色是第k人,出圈四步骤如下:
{
cout<<i+1<<" "; //1.输出位置号码 ,这里i是在数组里的编号,故输出要记得+1
a[i]=1; //2.将其标记成已出圈
num=0; //3.计数器num标记成0
sum--; //4.总人数减一
}
i++;
}
return 0;
}
//9 18 27 6 16 26 7 19 30 12 24 8 22 5 23
2.顺序表:(模拟解法)
题目要求顺序存储结构,那我这里就想办法改编的像当时学数据结构时的顺序表好了
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node{
int *a=NULL;
int sum;//圈内人数
};
void init(node &L,int n)
{
//L.a=new int[n];
//L.a=(int*)malloc(n*sizeof(int));
L.a=(int*)calloc(n,sizeof(int));
//cout<<sizeof(L.a)<<endl;
//memset(L.a,0,n*sizeof(L.a));//0表示在环中
L.sum=n;
}
void destroy(node &L)
{
delete L.a;
}
void ysf(node &List,int n,int k)
{
int i=0,num=0; //当前角色对应编号 计数器
while(List.sum>15)//总人数大于15游戏继续,这里15可以根据情况不同调节
{
i=i%n;//i是从0开始的,i到了n就归零
if(!List.a[i]) num++;//当前角色在圈中,则计数++
if(num==k)//当前角色是第k人,出圈
{
cout<<i+1<<" ";//输出位置号码 ,这里i是在数组里的编号,故输出要记得+1
List.a[i]=1;// 将其标记成已出圈
num=0;//计数器num标记成0
List.sum--;//总人数减一
}
i++;
}
destroy(List);
}
int main()
{
int n,k;//总人数 报数上限
cin>>n>>k;
node List;
init(List,n);
ysf(List,n,k);
return 0;
}
3.顺序表:(有带删除操作)
感觉代码2还是没有当时学顺序表的感觉,这里就又换了个形式
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node{
int *a=NULL;
int length;//报数序列 即计数器
int size; //存储总大小
int sum ; //当前存活人数
}L;
void init(int n,int k)
{
L.a=new int[n];
L.length=0;
L.size=n;
L.sum=n;
for(int i=0;i<L.size;i++)
{
L.a[i]=i+1;
}
}
void delete_List(int k)
{
for(int i=k;i<L.sum;i++)
{
L.a[i-1]=L.a[i];
}
}
void ysf(int n,int k)
{
while(L.sum>15)//这里可根据实际情况调节、
{
L.length=L.length+k;
if(L.length>L.sum)
L.length%=L.sum;
if(L.length==0) L.length=L.sum;
cout<<L.a[L.length-1]<<" ";
delete_List(L.length);
L.length--;
L.sum--;
}
}
int main()
{
int n,k;//总人数 报数上限
cin>>n>>k;
init(n,k);
ysf(n,k);
return 0;
}
4.带头循环链表:
题目要求链式存储结构,实现形式可以多种多样,这里例举一种符合链表思维的一种形式:
#include <bits/stdc++.h>
using namespace std;
//本题采用循环链表解决 约瑟夫环问题
typedef struct node{
int alive;
node *next;
}*pnode;
void init(pnode &head)
{
head = new node;
head->alive=-1;//头结点不参与计数
head->next=NULL;
}
void build(pnode head,int n)
{
pnode p=head;
for(int i=1;i<=n;i++)//为使链表依次从小到大,采用尾插法
{
pnode a=new node;
a->alive=i;
head->next=a;
a->next=NULL;
if(n==i) a->next=p->next;//这里采用的是带头结点的循环链表表示形式
head=a;
}
//head=p; 这里形参head若是指针的引用 \
这个函数中关于head的操作会影响到其实际的值 ,这样就要加上前面那条语句
}
void delete_node(pnode pre,pnode p)
{
pre->next=p->next;
p->next=NULL;
delete p;
}
pnode find(pnode p,int k)
{
pnode pre=NULL;
while(k--)
{
pre=p;
p=p->next;
}
if(p)
{
cout<<p->alive<<" ";
delete_node(pre,p);
}
return pre;
}
void ysf(pnode head,int sum,int k)
{
pnode p=head;
while(sum>15)//这里15可根据实际情况调节
{
p=find(p,k);
sum--;
}
}
int main()
{
pnode head=NULL;//node* head
init(head);
int n,k;
cin>>n>>k;
cout<<head<<endl;
build(head,n);
cout<<head<<endl;
ysf(head,n,k);//头指针 开始总人数 报数上限
}
5.递归解法
最后分享再分享一个递归解法
代码来源于:https://blog.youkuaiyun.com/yanweibujian/article/details/50876631
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int ysfdg(int sum,int value,int n)
{
if(n==1)
return (sum+value-1)%sum;
else
return (ysfdg(sum-1,value,n-1)+value)%sum;
}
int Joseph(int sum,int value,int n)//剩余人总数 报数上限 扔下海的第n个人
{
if(n==1)
return value%sum;//这么写会有值为零
else
return (Joseph(sum-1,value,n-1)+value)
}
int main()
{
int sum=30,alive=15,i,count=9;
for(i=1;i<=sum-alive;i++)
printf("第%2d个被扔海里人的编号:%2d\n",i,ysfdg(sum,count,i)+1);
return 0;
}
先分享这点吧,方法肯定不止这些,但我觉得其实掌握模拟方法,理解递归方法就足够了
ㄟ( ▔, ▔ )ㄏ