• 【洛谷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 }
  • 相关阅读:
    SSH免密登陆
    Linux服务器绑定多网卡IP
    搭建简易网站
    Linux中raid磁盘阵列
    Linux中防火墙命令
    Linux中LVM逻辑卷管理
    Linux中fdisk分区
    Linux计划任务
    Linux基础命令(三)
    Linux基础命令(二)
  • 原文地址:https://www.cnblogs.com/rabbit1103/p/14146600.html
Copyright © 2020-2023  润新知