问题描述
问题描述:有M(M>10)个敢死队员要炸掉敌人的一碉堡,谁都不想去,排长决定用轮回数数的办法来决定哪个战士去执行任务。如果前一个战士没完成任务,则要再派一个战士上去。现给每个战士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到8时,对应的战士就去执行任务,且此战士不再参加下一轮计数。如果此战士没完成任务,再从下一个战士开始数数,被数到第8时,此战士接着去执行任务。以此类推,直到任务完成为止。
排长是不愿意去的,假设排长为最后第M号,请你设计一程序,求出从第几号战士开始计数才能让排长最后一个留下来而不去执行任务。
分析
我的上篇文章(http://blog.csdn.net/hg_zhh/article/details/53819791)讲述了,一般见到此问题的思路:从每个人开始,都数一次,当最后剩下的人是排长时,输出,如果中途排长被数到,则跳出,尝试从下一个人数.
然而这种暴力求解的方式在m略大的情况时,时间和空间复杂度都不乐观.通过尝试不难发现. 如果假设从第一个人开始数,最后一个剩下的序号记为temp;总人数有m,则得出一个公式answer=(m-temp+1)%m;
而answer即为题目所求的结果;
此公式我是利用下图计算得出的:
从其中不难发现从第几个开始数,与最后剩余的是第几个,这两个变量间是存在线性关系的,只需加以推导就能得出。
下面这个代码与我上次上传的有些许差异,主要在于本次代码,只需要初始化一次链表结构即可。
代码实现
#include<stdio.h>
#include<malloc.h>
struct List {
int data;
struct List *next;
};
struct List * CreatList ()//建立空链表
{
struct List *L;
L=(struct List *)malloc(sizeof(struct List));
if (L==NULL)
{
printf("内存分配失败\n");
exit(0);
}
L->next=NULL;
return L;
}
void InitList (struct List *L,int n)//将L初始化为无 头结点的链表
{
struct List *P,*T ;
int i;
T=L;
T->data=1;
T->next=NULL;
for ( i=2;i<=n;i++)
{
P=(struct List *)malloc(sizeof(struct List ));//申请空间
if (P==NULL)// 如果不成功,则退出程序
{
printf("内存分配失败\n");
exit(0);
}
P->data=i;
P->next=NULL;
T->next=P;
T=P;
}
P->next=L;
}
int deleteNode(int m)//假设从第一个人开始数时,返回最后一个出去的人的序号
{
struct List *L,*temp,*p;
L=CreatList();
InitList(L,m);
p=L;
int i,j;
for (j=0;j<m-1;j++)
{
for (i=1;i<8-1;i++)
{
p=p->next;
}
temp=p->next;
p->next=temp->next;
free(temp);
p=p->next;
}
return p->data;
}
void answer ()
{
int m;
printf("共有多少人、\n");
scanf("%d",&m);
int num=deleteNode(m);
int temp=(m-num+1)%m;//此公式是此程序最关键的地方
printf("从第%d个数\n",temp);
}
int main()
{
answer();
return 0;
}
总结:
此类问题,关键在于得出一般公式。诸位可以尝试排长排在第a个位置的情况(我还没尝试过)。