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

输入
输入正整数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);
}
}