【洛谷P1145】约瑟夫
问题描述
n 个人站成一圈,从某个人开始数数,每次数到 m 的人就被杀掉,然后下一个人重新开始数,直到最后只剩一个人。现在有一圈人,k个好人站在一起,k 个坏人站在一起。从第一个好人开始数数。你要确定一个最小的 m,使得在第一个好人被杀死前,k 个坏人先被杀死
输入格式
一行一个整数 k。
输出格式
一行一个整数 m。
样例输入
#1
3
#2
4
样例输出
#1
5
#2
30
数据范围
0<k<14。
题解
最朴素的算法就是一个一个数,用一个计数器记录当前报的数,再用一个指针指向当前报数的人,如果当前位置上的人已经出局,就把指针往后移,直到找到第一个没有出局的人。
报数是循环报的,即最后一个人报完后,下一个数由第一个人报,为了方便循环的实现,我们从0开始编号,这样每次只要把指针加一再对总人数取模,就可以实现循环报数
考虑优化
我们只需要最后留下的都是好人,并且我们已经知道好人在前,坏人在后,这个相对位置是不会变的,而每个人最初的序号可以忽略
假设最初大家围成一个大圆,每个人坐在一把椅子上,每次淘汰一个人就把那个人的椅子搬走,把围成的圆缩小,再对每个人重新编号,这样保证了每一次淘汰操作前所有人的编号是连续的
由于我们只要淘汰报m的人,我们可以以报m个数为一个循环,淘汰掉最后一个人。前面已经实现每一次淘汰前所有的人的编号是连续的,所以只要记录上一次淘汰的人的位置,把这个位置加上m再对当前总人数取模就可以直接算出当前循环要淘汰的人
1 #include <cstring> 2 #include <cstdio> 3 int main() 4 { 5 int n,m,k,t,cnt,ok,i,j,num; 6 scanf("%d",&k); 7 n=k*2; 8 for (m=k;;m++) 9 { 10 t=0; ok=1; 11 for (j=1;j<=k;j++) 12 { 13 num=n-j+1; 14 if ((t+m)%num<k) 15 { 16 ok=0; 17 break; 18 } 19 t=(t+m)%num; 20 } 21 if (ok) 22 { 23 printf("%d\n",m+1); 24 break; 25 } 26 } 27 return 0; 28 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具