二分算法题目训练(一)——Shell Pyramid详解

版权声明:未经博主允许不得转载,引用,盗用 https://blog.csdn.net/EliminatedAcmer/article/details/81509584

HDU2446——Shell Pyramid 详解

  • Shell Pyramid
  • 题目描述(Google 翻译的)
    在17世纪,由于雷鸣般的喧嚣,浓烟和炽热的火焰,海上的战斗与现代战争一样。但那时,大炮非常简单。它就像一个铁缸,其后端密封,前端打开。它的后端有一个小孔,用来安装保险丝。战舰上的大炮被放在有四个轮子的小型车辆上,炮弹是铁球,里面装着火药。

    当时,据说有一位聪明的船长,他也是一位数学家的业余爱好者。他喜欢把他遇到的所有东西都连接到数学上。在每次战斗之前,他经常命令士兵将炮弹放在甲板上并使这些炮弹形成炮弹金字塔。

    现在让我们假设一个壳金字塔有四层,每层都会有一系列序数。它们如下图所示:
    这里写图片描述

    在该图中,它们分别是从左到右的第一层,第二层,第三层和第四层。

    在第一层中,只有1个壳,其序数为1.在第二层中,有3个壳,它们的序数为1,2和3.在第三层中,有6个壳,它们的序数分别为1,2,3,4,5和6.在第四层中,有10个壳,它们的序数在上图中显示。

    整个贝壳金字塔也有序列号。例如,第二层中第三个外壳的序列号为4,第三层中第五个外壳的序列号为9,第四层中第九个外壳的序列号为19。

    还有一个相互关联的问题:如果给出一个序列号s,那么我们可以计算出第s个shell是在什么层,什么行和什么列。假设层数是i,行数是j而列数是k,因此,如果s = 19,则i = 4,j = 4并且k = 3。

    现在让我们继续讲述船长的故事。
    一场战斗即将开始。船长给每个大炮分配了相同数量的炮弹。炮弹堆放在甲板上,由炮弹形成相同的炮弹金字塔。当敌人的战舰靠近时,船长命令同时开火。然后听到了雷鸣般的声音。船长仔细听了,然后他就知道有多少炮弹被使用了,剩下多少炮弹了。

    在战斗结束时,船长赢了。在休息期间,他向下属询问了一个问题:对于壳金字塔,如果给出序列号s,你如何计算层数i,行号j和列号k?

  • 输入
    首先输入一个数字n,再进行n个案例。对于每种情况,都有一个足够大的壳金字塔,给出一个整数,这个整数是序列号s(s <2 ^ 63)。有几个测试用例。输入由文件末尾终止。
  • 输出
    对于每种情况,输出相应的层编号i,行编号j和列编号k。
  • 样例输入
    2
    19
    75822050528572544
  • 样例输出
    4 4 3
    769099 111570 11179
  • 思路分析
    • 首先通过二分先确定给出的数字是哪个金字塔,要找到是哪个金字塔,就需要知道给定序号代表的意思,序号是宏观的炮弹下标,因此它必然代表之前已经出现的所有炮弹,要找到它属于第几个金字塔,就需要一个数组来存储对于金字塔序号 i,它和它之前所有的金字塔会产生多少炮弹。
    • 再通过二分确定是属于这个金字塔的哪一层。要找到它是哪一层,就需要知道这个金字塔有多少个炮弹,因此还需要一个数组来存储对于金字塔序号 i,它自己会产生多少炮弹。
  • 设计思路
    1. 对于第 i 个金字塔,它所包含的炮弹数都是第 i-1 个金字塔所包含的炮弹数加上 i,比如第二个金字塔有 3 个炮弹,那么第三个就有 3+3 个炮弹,因此得到非递减序列 a[i] = a[i-1] + i
    2. 对于第 i 个金字塔,所有的炮弹数目为 i-1 个金字塔所包含炮弹数目与自己含有炮弹之和,因此得到非递减序列 sum[i] = sum[i-1] + a[i]
    3. 接下来只需要两次二分找出结果,找到金字塔序号 p 后,减去sum[p-1],就会得到所在金字塔中第多少个炮弹,找到行号 row 后,减去a[row-1],就是列号。
#include <iostream>
#include <cstdio>
#include<cmath>

#define Maxn 1000000

using namespace std;

typedef long long int LL;

LL a[Maxn];
LL sum[Maxn];

LL find_index(LL *array,LL size,LL key);

int main()
{
    int i;
    a[1] = 1;
    for(i=2; i<Maxn; i++)
    {
        a[i] = a[i-1]+i;
    }
    sum[1] = 1;
    for(i=2; i<Maxn; i++)
    {
        sum[i] = sum[i-1]+a[i];
    }
    int t;
    scanf("%d",&t);
    while(t--)
    {
        LL num;
        cin >> num;
        LL p = find_index(sum,Maxn,num);
        num -= sum[p-1];
        LL row = find_index(a,Maxn,num);
        LL col = num-a[row-1];
        cout<<p<<" "<<row<<" "<<col<<endl;
    }
    return 0;
}
LL find_index(LL *array,LL size,LL key)
{
    int first = 0, last = size-1;
    int middle, pos=0;

    while(first < last)
    {
        middle = (first+last)/2;
        if(array[middle] < key)
        {
            first = middle + 1;
            pos = first;
        }
        else
        {
            last = middle;
            pos = last;
        }
    }
    return pos;
}
  • 对于非递减序列,可以使用 STL 中的 lower_bound()
#include <iostream>
#include <cstdio>
#include<cmath>

#define Maxn 1000000

using namespace std;

typedef long long int LL;

LL a[Maxn];
LL sum[Maxn];

int main()
{
    int i;
    a[1] = 1;
    for(i=2;i<Maxn;i++)
    {
        a[i] = a[i-1]+i;
    }
    sum[1] = 1;
    for(i=2;i<Maxn;i++)
    {
        sum[i] = sum[i-1]+a[i];
    }
    int t;
    scanf("%d",&t);
    while(t--)
    {
        LL num;
        cin >> num;
        LL p = lower_bound(sum,sum+Maxn,num)-sum;
        num -= sum[p-1];
        LL row = lower_bound(a,a+Maxn,num)-a;
        LL col = num-a[row-1];
        cout<<p<<" "<<row<<" "<<col<<endl;
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/EliminatedAcmer/article/details/81509584
今日推荐