有n个人围成一圈,顺序从1开始顺序编号。从第一个人开始报数(从1报到3),凡报到3的人退出圈子,问

有n个人围成一圈,顺序从1开始顺序编号。从第一个人开始报数(从1报到3),凡报到3的人退出圈子,问最后留下来的是原来的是第几号。用C语言指针完成编程。

此题可用数学方法求解。


设有n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数  (用数学方法解的时候需要注意应当从0开始编号,因为取余会取到0解。)


实质是一个递推,n个人中最终留下来的序号与n-1个人中留下来的人的序号有一个递推关系式。


假设除去第k个人,则


0, 1, 2, 3, ..., k-2, k-1, k, ..., n-1          // 原始序列 (1)


0, 1, 2, 3, ..., k-2,      , k, ..., n-1        // 除去第k人,即除去序号为k-1的人   (2)


k, k+1, ..., n-1,    0,    1,        ..., k-2  // 以序号k为起始,从k开始报0  (3)


0, 1,     ..., n-k-1, n-k, n-k+1, ..., n-2   // 作编号转换,此时队列为n-1人  (4)



换后就完完全全成为了(n-1)个人报数的子问题,注意(1)式和(4)式,是同一个问题,不同的仅仅是人数。比较(4)和(3),不难看
出,0+k=k, 1+k=k+1, ... ,(3)式中'0'后面的数字,((n-3)+k)%n=k-3,((n-2)+k)%n=k-2,
对于(3)式中'0'前面的数字,由于比n小,也可看作(0+k)%n=k,  (1+k)%n=k+1,  故可得出规律:


设(3)中某一数为x' , (4)中对应的数为x,则有:x'=(x+k)%n.


设x为最终留下的人序号时,队列只剩下1人时,显然x=0; 此时可向前回溯至2人时x对应的序号,3人时x对应的序号……直至n人时x的序号,即为所求。

#include <stdio.h>
const int M = 3;
int main()
{
    int n, s = 0;
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i)
        s = (s+M)%i;
    printf("%d\n", s+1);
    return 0;
}

温馨提示:内容为网友见解,仅供参考
第1个回答  2008-07-18
#define nmax 50
main()
{
int i,k,m,n,num[nmax],*p;
printf("please input the total of numbers:");
scanf("%d",&n);
p=num;
for(i=0;i<N;I++)
*(p+i)=i+1;
i=0;
k=0;
m=0;
while(m<N-1)
{
if(*(p+i)!=0) k++;
if(k==3)
{ *(p+i)=0;
k=0;
m++;
}
i++;
if(i==n) i=0;
}
while(*p==0) p++;
printf("%d is left\n",*p);
}
第2个回答  2008-07-18
Joseph问题...讲链表的时候都会讲到吧
网上随便搜一下都会有解答的啦XD本回答被提问者采纳

...从第一个人开始报数(从1报到3),凡报到3的人退出圈子,问
设有n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数 (用数学方法解的时候需要注意应当从0开始编号,因为取余会取到0解。)实质是一个递推,n个人中最终留下来的序号与n-1个人中留下来的人的序号有一个递推关系式。假设除去第k个人,则 0, 1, 2, 3...

...从第一个人开始报数(从1到3报 数),凡报到3的人退出圈子,
call_n++;\/\/报数 call_n%=3;\/\/最大为3,到了3就从0开始 if(call_n==0){*p=1;out_n++;}\/\/为0(即3)出局 } p++;if(p==a+N)p=a;\/\/循环转向下一人 } printf("最后剩余者的编号是:%d\\n",p+1-a);}

...从第一个人开始报数(从1到3报数),凡报到3的人退出 圈子,问最后留下...
就像题目中描述的那样,每次循环之后,数到3的人都被淘汰,其他的人构成一个新的圈。该题目中:if(*(p+i)!=0) k++;就是实现了,那些没有被淘汰的人(数组的对应元素值不为0)围成一个圈。但是,虽然被淘汰的人不再参与围成一个圈,但是,每次都要逐一判断这n人是否被淘汰,i就是用来记...

...从第一个人开始报数(从1报到3),凡报到3 的人退出圈子,问最后留下的...
for(i=0;i<n;i++) \/\/此处的for循环是给这n个人编号:第一个是1,第二个是2,……而且,仍然保持p指向数组num的起始处 (p+i)=i+1;i=0;k=0;m=0;while(m<n-1) \/\/此处的while循环是挑选报3的人退出圈子 { if(*(p+i)!=0) k++; \/\/初始从i=0开始不空则k加一 if(k...

有n个人围成一圈,顺序排号。从第1个人开始报数(从1到3),凡报到3的人...
int main(void){ int a[N];int num = 1;int count = 0;for(int i = 0; i < N; ++i, ++num){ if(num == 4){ num = 1;} if(num == 3){ ++count;} a[i] = num;} while(count != N-1){ count = 0;for(i = 0; i < N; ++i){ if(a[i] == 3){ count...

...从第一个人开始报数(从1到3报 数),凡报到3的人退出圈子
int out = 0; \/\/退出的人数 int num = 0; \/\/报数 int a[1024] = {0}; \/\/0表示退出圈子 printf("Input n:");scanf("%d", &n);for (i = 0; i < n; i++){ a[i] = 1;} i = 0;while (out != n-1){ if (a[i] == 1){ num++;} if (num == 3){ a[i]...

...从第一个人开始报数(从1到3报数),凡报到3的人退出 。。。
n个人围成一圈,按顺序编号,分别为1、2、3..n。(你可以理解成每个人的座号)。然后1号开始,每人依次报号。即 (这里假设n=5)(前面是座号、后面是他报的号)1:1 2:2 3:3(退出)现在只剩下座号为1、2、4、5的人,从3的下一个开始报号 4:1 5:2 1:3(退出)2:1 4:2 5:3...

...从第一个人开始报数(从1到3报数),凡报到3的人退出
public class Increase {public static void rep(boolean[] people) {int i = 0,j=0,n=people.length,m=n;while(n>2){i=++i%m;if (people[i] == true){j++;if (j==3){people[i] = false;System.out.println(i);n--;\/\/总人数减1j = 0;\/\/到3从头数}}}public static void...

...有n个人围成一圈,顺序排号,从第一个人开始(从1-
{ char student[N];\/\/N个人,1表示未退出,0表示已经退出 int out[N];\/\/退出的人的号码记录 int count = -1;\/\/循环计数 0,1,2 <=> 1-3 int num_out = -1;\/\/退出号码记录的数组的下标 memset(student, 1, sizeof(student));\/\/将所有人状态置为1(未退出)for (int i = 0;...

...从第一个人开始报数(从1~3报数)凡报到3的人退出圈子
代码有错,横线上填什么都不会过编译。把if(p>(___)改成if(p>(___))或if(p>___)的话,依次填:a+1、N、a+N、*p!=0或*p、i-3或3-i、 a[i]或a[i]!=0就能达到目的。第二个for循环中的j变量显然是多余的!

相似回答