清北学堂第一题 游乐园 SmartOJ 1815(二分查找的精髓)

来源:互联网 发布:前列腺高潮经验知乎 编辑:程序博客网 时间:2024/04/28 05:11
本人原创日志,允许转载,但请注明出处,谢谢!

题目描述

游乐园里新推出一个游戏——飞机驾驶体验。有N-1个小朋友来玩这个游戏,他们排成长长的一列。这个游戏一共有M飞机可供驾驶,每架飞机的使用时间都是有限制的,第i飞机的使用时间是si分钟。
一开始所有飞机都是闲置着的,然后排队等候着的小朋友依次上飞机。如果有飞机闲置,则小朋友一定会上去玩(小朋友不会在意飞机使用的时间);如果同时有多架飞机闲置着,小朋友一定会选择标号最小的那架。
现在你带着你的弟弟来到了游乐园,排到了最后一个位置。你想知道你弟弟会在哪架飞机上体验。

输入格式

第一行两个数NM
接下来一行M个数,第i个表示si

输出格式

输出一个数,表示你弟弟会坐上哪架飞机。

样例输入

22 5

1 2 3 4 5

样例输出

4

数据范围与提示

数据规模对于30%的数据 N≤1 000 000;对于100%的数据 1≤N≤1 000 000 0001≤M≤1 000

数据范围如此之庞大,循环暴力解决是绝对不行的!!!!!!!!就连单层循环都不允许~~~~~为此我哭了一阵!!!

程序代码解析

解析:

1)如果完成一件事一次需要b分钟,那么如果总共用n分钟,那么共完成了几次,第n分钟正在做第几次(不一定完成)。

技巧:①共完成了n/b次。

     n分钟正在做第(n+b-1)/b次。

②证明:假设n/b=k,n%b=y;

       那么,可知如果y==0,那么总共就做了k次,且第k次恰好完成;如果y!=0,那么总共做了k+1次,但是第k+1次正在做,还没有完成。

       为了避免余数的讨论,进行这样的处理(n+b-1)/b

       而:(n+b/bn/b是等效的,只是商k+1而已。

如果原先n恰好足够b,那么n-1一定不足b(n-1)%b=y-1

故在原来的基础上加上一个b-1之后,与原来等价。不足b时结果加1.

2)二分法的精髓:二分并不一定局限于对有序数列的查找,对于无需数列,可以转换角度建立二分模型,将不能二分的情况转化为二分的模型,如本题中用下标与总时间建立模型进行二分。当然这建立在(1)可求的基础上。

以下代码摘自:http://blog.163.com/sentimental_man/blog/static/730016182011931103428207/

#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long I64;
const int MAXN = 1001;
const int INF = 0x7fffffff;
int g_n, g_m, g_time[MAXN], g_min;

//输入并找出最小值
void Input()
{
    scanf("%d%d", &g_n,&g_m);
    int i;
    g_min = INF;
    for (i = 0; i < g_m; i++)
    {
        scanf("%d",&g_time[i]);
        if (g_time[i] < g_min) g_min =g_time[i];
    }
}
I64 GetNumber(I64 minite) //
统计minite分钟内能容纳小朋友的总人数 
{
    int i;
    I64 number = 0;
    for (i = 0; i < g_m &&number < g_n; i++)
        number += (minite + g_time[i] -1) / g_time[i];      //
用的就是技巧(1)
    return number;
}
I64 FindTime() //
找到n这个人玩飞机的时间
{
    I64 l, r, mid, num;
    l = 0, r = ((I64) g_min) * g_n; //
开始的时候这边忘记类型转化,导致数据溢出,第五组数据一直过不了!在整数和长整数混用时一定要注意!
    while (l < r)
    {
        mid = (l + r) / 2;
        num = GetNumber(mid);
        if (num >= g_n)
            r = mid;
        else
            l = mid + 1;
    }
    return r;
}
int FindId(I64 minite) //
寻找所玩飞机的编号
{
    int i;
    I64 t_n = g_n;
    t_n -= GetNumber(minite - 1);      //
得出最后一分钟内有多少个小朋友正在玩飞机
    minite--;
    for (i = 0; i < g_m; i++)
    {
        if (minite % g_time[i] == 0)    //
如果求余为0,那么一定会有小朋友上飞机
        {
            t_n--;
            if (t_n == 0) return i + 1;
        }
    }
    return -1;
}
int main()
{
    Input();
    I64 time = FindTime();     //
先二分找出n这个人玩飞机的时间
    int id = FindId(time);     //
根据这个时间找出n小朋友玩的飞机
    printf("%d\n", id);
//  system("pause");
    return 0;
}


时间复杂度基本能达到O(log(2,n));

我带着Orz的心情笑了!!!!!!!!!!!~~~

0 0
原创粉丝点击