Description
有m个猴子围成一圈,按顺时针编号,分别为1到m。现打算从中选出一个大王。经过协商,决定选大王的规则如下:从第一个开始顺时针报数,报到n的猴子出圈,紧接着从下一个又从1顺时针循环报数,...,如此下去,最后剩下来的就是大王。
Input
第一行是一个正整数T表示测试数据的组数。下面共有T行,每行两个整数m和n,用一个空格隔开,分别表示猴子的个数和报数n。1≤m≤100,1≤n≤200。
Output
每组数据对应有一个输出,表示大王的编号。
Sample Input
1
3 2
Sample Output
3
显而易见,这又是经典的约瑟夫环问题。
关于约瑟夫环有很多解法,网上也有很多优秀的解法代码,以下只是个人解题理解,仅供参考。
循环链表--解决约瑟夫环问题
*首先一个需要注意的地方,建立链表,按照老师上课讲的方法,头节点后边跟数据虚拟节点,然后才跟数据元素节点
*图片来自于王洪峰老师课件
但是考虑到两个地方,一是本题所用链表头节点不需要存放关于链表的信息,所以可以用数据节点当作头节点,不需要定义专门的头节点结构类型;二是本解法是循环链表,为了节约时间(主要是我懒!- -)。所以,我直接采用虚拟数据节点当作头节点,未专门定义头节点数据类型,看代码时候需注意。
解题思路:
1.创建链表,对链表各个节点编号,读入数据完成后,尾节点指针域指向第一个数据节点,构成循环链表
2.遍历链表并计数,计数到n-1的时候,删除下一个节点,计数清零。如此循环,直到只剩一个节点(也就是两个相邻节点数据相等时),跳出循环。
3.指针带回最后所剩的数据,打印输出。
整个思路很简单,关键是循环链表的构建不能出错!
具体实现代码及详细注释:
#include<stdio.h> #include<malloc.h> typedef struct LDataNode { int number; struct LDataNode *next; }LDataNode,*LinkList; int InitList(LinkList *head); int ListCreate(LinkList *head,int n); int ListDelete(LinkList *head,int *flag); //初始化 int InitList(LinkList *head) { *head = (LinkList)malloc(sizeof(LDataNode)); if(*head == NULL) { return -1; //内存分配失败 } (*head)->next = NULL; return 0; } //读入数据 int ListCreate(LinkList *head,int n) { //尾插法原理 LinkList rear=(LinkList)malloc(sizeof(LDataNode)); rear=*head; for(int i=1;i<=n;i++)//编号进入链表 { LinkList p=(LinkList)malloc(sizeof(LDataNode)); p->next=rear->next; p->number=i; rear->next=p; rear=p; } rear->next=(*head)->next;//这一步是重点,构建循环!! } //删除节点 int ListDelete(LinkList *p,int *flag)//flag带回所删除的数据,备份 { if((*p)->next->number==(*p)->number)//两个相邻节点数据相同 { *flag=(*p)->number;//说明只剩一个节点了 return 1; } //删除操作 *flag=(*p)->next->number; LDataNode *q = (*p)->next->next; (*p)->next=q; return 0; } int main() { int T,m,n; scanf("%d",&T); while(T--) { scanf("%d%d",&m,&n); LinkList head; InitList(&head); ListCreate(&head,m); LDataNode *p=(LDataNode *)malloc(sizeof(LDataNode)); p=head; int count=0,x; while(1) { p=p->next; count++;//计数 if(count==n-1) { count=0; //删除第n个节点 int flag=ListDelete(&p,&x); if(flag) { break; } } } printf("%d\n",x); } }