• Joseph Problem


    前言


       这几天学习了队列,于是尝试了一下Joseph Problem,然后一直有一个地方没有通过,终于经点拨后,通过了。对于队列的使用还是加深了印象。建议如果队列的练手可以去尝试解决这个问题。

    正文


     

     

    Joseph Problem(35分)
     
    铺垫:
     
    假设有N个人围成一圈,然后第一个人把第二个人杀了,刀子给第三个人,第三个人把第四个人杀了,以此类推......
     
    首先注意:难点1:围成一圈,我们会想用什么数据结构呢?这么多人,首先应该想到用数组对不对,可是怎么表示围成一圈呢?重点来了,其实我们可以不断的把前面的挪到后面不就相当于围成了圈吗?对不对?
     
    难点二:前一个人把后一个杀了?普通的应该会想把这个被杀的人的元素去掉,然后前一个元素往后面移,可是这样就达不到围成圆圈的效果了,重点来了,就是把这个杀人的人一开始也杀了,对!没看错,是把它也杀了,然后再把它放到放到数组的最某端就可以了。
     
    好,难点讲诉完毕。
     

    题目内容:

    實作Joseph problem.

    假設一開始有N個人,編號1~N,

    按照順序以順時針圍成一個圓圈。

    遊戲開始時,編號1的人拿刀。

    之後每一輪刀子會被往下傳M個人,

    而當輪最後拿到刀子的人會將他的下一個人殺掉,

    殺完後刀子會再傳給被殺的下一個人。

    這樣一輪就算結束。

    遊戲會進行許多輪,直到只剩下最後一個人。

     

    範例1:N=5, M=2

    第一輪:刀子傳給3號,4號被殺,刀子再傳給5號 (1 2 3 5)

    第二輪:刀子傳給2號,3號被殺,刀子再傳給5號 (1 2 5)

    第三輪:刀子傳給2號,5號被殺,刀子再傳給1號 (1 2)

    第四輪:刀子傳給1號,2號被殺,最後1號存活。

    範例2:N=4, M=3

    第一輪:刀子傳給4號,1號被殺,刀子再傳給2號 (2 3 4)

    第二輪:刀子傳給2號,3號被殺,刀子再傳給4號 (2 4)

    第三輪:刀子傳給2號,4號被殺,最後2號存活。

     

    输入格式:

    輸入第一行為一個數字T,代表測資的筆數。

    接下來會有T筆測資,每一筆測資一行,

    會有兩個數字N,M,數字間以空格區隔。

    數字範圍:

    T < 1000

    0 < N <= 1000

    0 < M <= 1000

     

    输出格式:

    輸出一行數字,將每筆測資最後存活下來的人的編號加總。

     

    输入样例:

    3

    5 2

    4 3

    8 4

     

    输出样例:

    4

    时间限制:1000ms内存限制:32000kb
     
     

    大家先尝试解一下。再看文章比较好!

     

    先理解题意:看第一个例子:刀子传两个人后到了第三个人,把第四个人杀了。加上又是圆圈,所以就相当与把两个人杀了挪到后面,然后就是成了前面的问题了对不对?好,开始写代码。

    首先我们考虑杀人这个动作。

    1 int Dequeue() {
    2     int z;//记录哪个人被杀,不然要是没有记录,怎么去把它挪到后面
    3     z = ch[Head];
    4     Head = (Head + 1) % 1000;//就是把Head指向后一人就可以了,相当于这一圆圈的人没有它了
    5     Number_of_Items--;//总人数减一
    6     return z;
    7 }

    再考虑插到后面去这个动作:

     1 void Enqueue(int x) {
     2     if(Number_of_Items == 0) {//如果数组是空的,重新定义队列的头和尾
     3         Tail = Head = 0;
     4         ch[0] = x;//然后把它加进去
     5     } else {
     6         Tail = (Tail + 1) % 1000;//队尾指向后面一个,用于存储新的人
     7         ch[Tail] = x;
     8     }
     9     Number_of_Items++;//总人数加一10 }

    接下来就是总体的设计了

            scanf("%d %d",&a,&b);//输入总人数和要传的人数
         
    for(int i=1; i <= a; i++) Enqueue(i);//把这几人放到数组里面去 for(int i=1; i <= a-1; i++) {//每一轮杀一个人,总共要杀n-1个人 for(int k=0; k < b%Number_of_Items; k++) { j = Dequeue(); Enqueue(j); }//每次把b个人挪到后面 j = Dequeue(); Dequeue(); Enqueue(j);//就像铺垫所说的,杀两个人,再把第一个人放到数组后面去。 }

    注意:

    1.增加数组元素时,注意要考虑当数组为空。

    2.

    for(int k=0; k < b%Number_of_Items; k++) {
                    j = Dequeue();
                    Enqueue(j);
                }

    注意这段代码,就是这个地方卡了我很久,看到没,当b非常大的时候,运行时间要的非常久,这里已经有3个for循环了,所以想法设法降低算法复杂度。于是发现可以对b进行优化,这是关键点。

    3.每一次进行加入数组元素时,总人数记得加一,删除数组元素时,总人数记得减一。

    好,接下来写出总代码:

    #include<stdio.h>
    int ch[1000];
    int Head,Tail,Number_of_Items;
     
    void Enqueue(int x) {
        if(Number_of_Items == 0) {
            Tail = Head = 0;
            ch[0] = x;
        } else {
            Tail = (Tail + 1) % 1000;
            ch[Tail] = x;
        }
        Number_of_Items++;
    }
     
    int Dequeue() {
        int z;
        z = ch[Head];
        Head = (Head + 1) % 1000;
        Number_of_Items--;
        return z;
    }
     
    int main() {
        int n,j,a,b;
        int answer;
        int sum = 0;
        scanf("%d",&n);
        for(int i=0; i < n; i++) {
            scanf("%d %d",&a,&b);
            ch[1000] = 0;//注意每一次对数组的更新
            Number_of_Items=0;//对总人数的更新
            Tail = Head = 0;
            for(int i=1; i <= a; i++)
                Enqueue(i);
            for(int i=1; i <= a-1; i++) {
                for(int k=0; k < b%Number_of_Items; k++) {
                    j = Dequeue();
                    Enqueue(j);
                }
                j = Dequeue();
                Dequeue();
                Enqueue(j);
            }
            sum += ch[Head];
        }
        printf("%d",sum);
        return 0;
    }

    注意:每次数组用完之后,都得进行更新赋值为0,并且对总人数也得更新。

    总结


      这题卡到的地方主要是如何降低算法复杂度,对其中一些变量进行优化。我也是经别人点拨才明白了这里的优化,还得多多学习。

     

    2016-03-06 14:14:02

     

     

  • 相关阅读:
    spring 源码解析一(bean定义)
    IOC 容器中添加组件的方式
    spring 后置处理器
    .NetCore3.1 配置Kestrel端口的几种方式及优先级
    CESIUM空间中AB两点A绕B点的地面法向量旋转任意角度后新的A点坐标(A’)
    Cesium坐标转换:根据两个坐标点(坐标点a、坐标点b)的经纬度,计算a点和b点的角度
    任意一个点A(x,y)围绕任意一个点B(a,b)旋转任意角度后的坐标值
    已知地球上的2点坐标,A和B,求A,B线上 任意点位置。
    centos6下mysql5.7.13制作rpm包
    sql 删除狐立帐户
  • 原文地址:https://www.cnblogs.com/ABC-00/p/5247389.html
Copyright © 2020-2023  润新知