【洛谷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
",m+1);
24             break;
25         }
26     }
27     return 0;
28 }
原文地址:https://www.cnblogs.com/rabbit1103/p/14146600.html