有n个人,顺序排列, 并首尾相连围成一圈。从第一个人开始报数(从1到4),凡报到4的人退出圈子+扩展版本

https://blog.csdn.net/qq_44037213/article/details/106551760?fps=1&locationNum=2
有n个人(n<=1000),用1,2,...,n编号,顺序排列,
 并首尾相连围成一圈。从第一个人开始报数(14),凡报到4的人退出圈子,
 且后面的人继续报数(同样从14报数),问最后留下的是原来第几号的那一位(用指针实现)。

输入
输入正整数n。

输出
输出最终留下的那个人的编号。

样例输入
53
样例输出
7


#include<stdio.h>
#define N 1000
int main()
{
	void dismiss(int m);
	int n;
	scanf("%d",&n);

	dismiss(n);
	return 0;
}
void dismiss(int m)//m位同学
{
	int i,j=0,t=0;
    int identifier[N];//保存编号identifier
	int *p = identifier;

	for(i=0;i<m;i++)
	{
		*(p+i)=i+1;//通过指针创建/修改了数组里的内容()
	}

//如果不对数组进行剪裁的话,则需要一个东西(这里定义了数码记录器j)来记录当前所报的数码(在1,2,3,4中循环)
//在编号位没有被摸为零的,可使所报数码+1每报到数码4的时候,作剔除(摸零);从头开始报(1,2,3,4)
/* 不得不说,这个for是巧妙的:
1.他的循环结束判断条件不是直接由循环变量i来控制的,
而是借助与另一个关键指标(变量t):被剔除的学生数目是否达到了(学生总数-1),即是否只剩一个同学在场了 
那么i加到什么时候是个尽头?*/
/* 这个for要实现当访问到尾的时候能够回到头部(通过 判断(是否末尾--若是,重置i  再扫描来实现) */
	for(i=0; t<m-1 ;i++)//运行m次
	{
        /* 该编号的同学是否已被抹去/有效,若有,使所报数码j++ */
		if(identifier[i]!=0)
			j++;

		if(j==4)//所报数值累计到4的同学://而且每达到4,重置j= 0;
		{
			*(p+i)=0;//赋值位0表示离开/抹去

			j=0;//重置数码记录器
			t++;//记录被抹去了多少位同学.当该数值达到n-1时,则只剩下一个同学了,结束循环
		}
        /* 判断是否到了结尾identifier[m-1] 了(最后一个元素的索引 m-1),处理回头问题. :
        通过重置i=-1为啥不是0?,因为回到for的时候会i++的identifier[0]..,*/
		if(i>=m-1)
			i=-1;
	}


	for(i=0;i<m;i++)
	{
		if(identifier[i]!=0)
		printf("%d",i+1);
	}
}


扩展版本:

#include <stdio.h>
#define N 1000
int main()
{
    void dismiss(int n,int m,int s);/*声明函数*/

    int n;/*学生规模*/
    int m;/*报到m的同学离场*/
    int s;/*从第s位同学开始报数*/
    printf("输入n(学生规模),m(报到m的同学离场),s(从第s位同学开始报数):\n");
    scanf_s("%d %d %d", &n,&m,&s);/*读入学生规模*/

    dismiss(n,m,s);/*调用处理函数*/
    return 0;
}
/*处理函数(包括打印)*/
void dismiss(int n,int m,int s) //m位同学
{
    int i, /*循环变量*/
        j = 0, /*报数数码记录器j */
        t = 0;/*记录已经离场的学生的数目*/
    int identifier[N]; //保存编号identifier
    int* p = identifier;
    /*初始化数组*/
    for (i = 1; i <= n; i++)/*[1,m]从1开始计数.*/
    {
        *(p + i) =1; //通过指针创建/修改了数组里的内容(生成编号.)
    }

    //如果不对数组进行剪裁的话,则需要一个东西(这里定义了数码记录器j)来记录当前所报的数码(在1,2,3,4中循环)
    //在编号位没有被摸为零的,可使所报数码+1每报到数码4的时候,作剔除(摸零);从头开始报(1,2,3,4)
    /* 不得不说,这个for是巧妙的:
1.他的循环结束判断条件不是直接由循环变量i来控制的,
而是借助与另一个关键指标(变量t):被剔除的学生数目是否达到了(学生总数-1),即是否只剩一个同学在场了
那么i加到什么时候是个尽头?这无所谓了.*/
/* 这个for要实现当访问到尾的时候能够回到头部(通过 判断(是否末尾--若是,重置i  再扫描来实现) */
    for (i = 1; t < n -1; i++)/*剩下一个人在场,共n-1个离场*/
    {
        /* 该编号的同学是否已被抹去/有效,若有,使所报数码j++;如果使用的是0/1状态数组,可以省去判断,直接用一句:j += state[i];即可. */
        /*if (identifier[i] != 0)
            j++;*/
        j += identifier[i];

        if (j == m) //所报数值累计到m的同学://而且每达到m,重置j= 0;
        {
            *(p + i) = 0; //赋值位0表示离开/抹去

            j = 0; //重置数码记录器
            t++;   //记录被抹去了多少位同学.当该数值达到n-1时,则只剩下一个同学了,结束循环
        }
        /* 判断是否到了结尾identifier[n] 了(最后一个元素的索引 n),处理回头问题. :
        通过重置i=0为啥不是1?,因为回到for的时候会i++的identifier[1]..,*/
        if (i >= n)/*若进入if,则说明第n个小朋友刚报数并检查过了*/
            i = 0;
    }
    /*打印结果:*/
    for (i = 1; i <= n; i++)
    {
        if (identifier[i] != 0)/*如果使用0/1 state[]数组,也是如此判断打印.*/
            printf("%d", i);
    }
}

posted @ 2024-08-24 17:44  xuchaoxin1375  阅读(38)  评论(0)    收藏  举报  来源