(新版)SJTU-OJ-1003. 在麦当劳配数据

题目描述

注意:本题可以使用的头文件仅限于cstdio iostream  cstring

梦回高三,小艾想起了几个月前背诵高考古诗文篇目的时光……

已经是晚上了。第二天有小测,可是小艾还有n篇古诗文没有背。这n篇古诗文都有一个对应的瞌睡值s_i(1 \leq i \leq n),也就是说,选择背诵篇目i会让小艾的瞌睡程度增加s_i​。小艾目前的瞌睡程度已经是t,而当小艾的瞌睡程度\geq k时,小艾会直接睡到明早,那可就没有更多的时间背了!注意,因为背了一半的文章也是没背出来的文章,所以恰好让小艾瞌睡程度\geq k的那篇也视作没背的。

好在小测考到每个篇目的概率是一致的,所以为了小测分数更高,小艾只需要背诵尽可能多的文章即可。小艾预计,自己背了的篇目明日小测一定能默写对,没背的篇目有10%的概率瞎写对。小测只考察这n篇古诗词默写,每篇各一题,每题分值相同,小测满分为100。

“夜如何其?夜未艾。”时间不早了,小艾对明天的小测还有些紧张。请帮助小艾计算熬夜背书后,明日的小测期望最多能得多少分吧!

输入格式

输入共两行。

第1行为三个整数n,t,k分别表示篇目数,小艾现在的瞌睡值,以及让小艾直接睡着的瞌睡值。

第2行为n个非负整数,表示这n篇古诗文的瞌睡值s_i​。

输出格式

输出共一行。

第1行为一个整数x,表示小艾的期望最大分数(向下取整)。

样例输入

5 0 10
2 4 6 10 1

样例输出

64

共5道题,故每题20分。小艾最多能背诵三篇文章(瞌睡值为2 4 1),期望得分为3 \times 20pt + 2 \times 10 % \times 20pt = 64pt

数据范围

对于40%的数据,

1 \leq n \leq 5000, 0 \leq s_i \leq 1e7

对于另外20%的数据,

1 \leq n \leq 1e5, 0 \leq s_i \leq 1e7

对于再另外40%的数据,

1 \leq n \leq 1e6, 0 \leq s_i \leq 1e4

对于全部数据,还有

0 \leq t, k \leq 1e9

题目吐槽

        首先这个人可真是个人才,要背那么多篇古文还不早点开始背,而且妄想一晚上背完全部……平时不好好学习。。。

         是不是像极了期末考试前的你………………

        最离谱的是,看完那个数据………………        

        。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

        是的,我确实很无语。。。

        乖乖,不至于吧,这古文量好像有点大呢,那个10^9我们姑且不评论,那估计是个卷王,就那个5000篇,也是够离谱啦吧,他真的背的完吗

        突然又想起sjtu的英语小测 嘶………………学不完了学不完了

        幸亏我下学期才学数据结构

        还是去睡觉吧,睡觉多好多香啊

        还有也不知道为啥这个题目名字叫麦当劳,莫非出题者是在麦当劳配的数据?突然香气来了2333

        只允许cstdio iostream  cstring,似乎目的是不允许你用sort函数,那就自己二分吧

        二分法yyds

        不吐槽了,总之就是肥肠离谱,还是看题目吧。

题目解答

         非常明显,这个题目就是四个步骤

  1. 把这些奇奇怪怪的古文按照瞌睡程度排个序o(╥﹏╥)o
  2. 从小到大开始背书吧  o(╥﹏╥)o,就像在sjtu英语小测前刷英语课文一样,当然选短的优先看
  3. 背不会那就去睡觉 ヾ(✿゚▽゚)ノ好耶,(有几次我都没看完还是躺平啦)
  4. 考多少那就得看运气啦()

        sort函数不让用可还行,使用方法在文末。

        似乎这个题目很适合学习一手排序方法,

        还是先看看最普通的方法吧冒泡排序:

        不妨先复习一下冒泡的原理吧,如果会请直接忽略下面部分:


>补充知识:冒泡排序的原理

        首先我们假设有一个数组(至于是从0还是从1开始取决于自己),第一行是数值,第二行是对应数列的编号

编号a_1a_2a_3a_4a_5 a_6a_7
数值8257190

         要想完成排序则需要经过起泡这个过程

        第一轮起泡:固定左边一个位置(绿色部分),从左往右扫描黄色部分的数字

编号a_1a_2a_3a_4a_5 a_6

a_7

数值8257190

        一旦发现比绿色部分元素小的数字,立即执行交换:例如a_2就需要发生交换,交换后如下图

编号a_1a_2a_3a_4a_5 a_6

a_7

数值2857190

         继续扫描,下一个要交换的是a_5

编号a_1a_2a_3a_4a_5 a_6

a_7

数值1857290

        继续扫描,下一个要交换的是a_7

编号a_1a_2a_3a_4a_5 a_6

a_7

数值0857291

        扫描完成,第一个位置已经确认,也是所有数组里面最小的数字,已经确认的用蓝色标识,然后绿色标识右移,开始第二轮起泡

编号a_1a_2a_3a_4a_5 a_6

a_7

数值0857291

        第二轮气泡类似第一轮,可以发现,第i轮气泡之后,a_i就会确认,这样下去,总共7轮即可完成排序

         思考:对于有n项的数列a_n需要多少回合?最多需要n^2次,所以时间复杂度为O(n^2)


>冒泡排序的解法 

        同理,用冒泡排序法解决问题的代码如下:

#include <iostream>
using namespace std;
int main()
{
    long long int n, t, k,temp;
    cin >> n >> t >> k;
    long long int array[n];
    for (int i = 0; i < n; i++)
    {
        cin >> array[i];
    }

    for (int x = 0; x < n; x++)
    {
        for (int y = 0; y < n-1; y++)
        {
            if (array[y + 1] < array[y])
            {
                temp = array[y + 1];
                array[y + 1] = array[y];
                array[y] = temp;
            }
        }
    }//冒泡排序

    for (int i = 0; i < n;i++)
    {
        
        t = t + array[i];
        if (t >= k)     
        {
            cout << i * 100 / n + (n - i) * 10 / n;
            break;
        }
    }
    system("pause");
    return 0;
}

        但是这个代码的复杂度过高,不适合用来解决问题 ,尤其是遇上这么能卷的卷王背书,那哪里hold的住啊,真是的

        所以,还是快速排序香即为二分法


>补充知识:二分法排序的原理

        二分法的基本思想是函数的递归,假设有一个长度是10的数列

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值5730421968

        1.首先我们从这10个数字里面随意取一个,如a_1,并将其保存到变量k中,变量k将会作为一个参考值用于后续的比较

           为了便于理解,我们不妨先把a_1清空,实际也可以不清空

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值  730421968

        2.定义low与high两个变量,low=1,high=10;high准备从右往左开始扫描(参照对象为k=5 )  

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值  730421968

        3.high从10开始往左扫描,只要大于或等于k就pass,小于k就停止,把这个元素放到a[low];扫描过的用黄色表示

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值  730421968

         high从右往左扫描,现在我们发现 a_7< k,那么a_7就应该放在a_1,low继续扫描

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值173042  968

         low从左往右扫描,又发现a_2比k大,所以调位置放在右边去啦

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值1  30427968

         high继续从右往左扫描,又发现a_6比k小,放左边去啦

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值12304  7968

         low继续从左往右扫描,发现 a_3,a_4,a_5\leq k,呵呵哈哈哈low一路疯狂自增到

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值12304  7968

         好啦low与high回合啦好耶ヾ(✿゚▽゚)ノ,在把k放到会和的a6里面去

编号a_1a_2a_3a_4a_5 a_6a_7a_8a_9a_{10}
数值123045796

 8

        聪明的你一定已经发现a_6的位置已经是对哒啦,现在要排序a_1~a_5a_7~a_{10}

        递归即可啦

        这个函数是负责排序的总函数

//排序函数,得到从low到high之间的元素按照递增的顺序排列的数组
//由于数组在函数直接传递的特性,操作与修改直接对原数组进行
void SortQuick(int a[], int low, int high)
{
    int mid;
    if (low >= high)    return;
    mid = divide(a, low, high);
    SortQuick(a, low, mid - 1);
    SortQuick(a, mid + 1, high);
}

         这个函数是负责返回low与high回合的位置

//divide函数用来排序,小于a[0]的放左边,大于a[0]的放在右边
int divide(int a[], int low, int high)
{
    int k = a[low];

    do{
        while (low < high && a[high] >= k)      --high;
        if (low < high) 
        {
            a[low] = a[high];
            ++low;
        }
        while (low < high && a[low] <=k)        ++low;
        if (low < high) 
        {
            a[high] = a[low];
            --high;
        }
    } while (low != high);
    a[low] = k;
    return low;
}

        测试结果是只能通过70%的数据

        o(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)o

        啊啊啊呜呜呜

        继续改,

void QuickSort(int l, int r)   //思想没有变,但是方法改进啦很多
{                              //l是开始排序的左端,r是开始排序的右端
    int mid = (l + r) / 2;     //取中值即可
    int i = l;                 //为什么要单独定义i与j?
    int j = r;                 //答:l,r是固定的,我们要进行排序,l是起点,r是终点
    int x = array[mid];        //在后面的过程有自增与自减的过程,但是排序起终点不变,
    do                         //i j的定义是用来服务后面的
    {
        while(array[i]<x)      //模拟前面所说的从左往右扫描,只要比mid小就pass
            ++i;               //!警告:必须要是array[i]<x,不能是<=
                               //理由为一旦array[mid]是最大,程序下标直接越界
        while(array[j]>x)      //模拟前面所说的从右往左扫描,只要比mid大就pass
            --j;               //!警告:必须要是array[j]>x,不能是>=
                               //理由为一旦array[mid]是最小,程序下标直接越界
        if (i <= j)            //运行到这里说明一个问题,从左往右、从右往左扫描都停止了
        {                      //停止意味着不满足条件的array[i]、array[j]
                               //说明:if(i <= j)必须为 <= 不能是<否则只有一个数时无法排序
           swap(array[i], array[j]);
           ++i;--j;            //显然,只要交换他们两个就可以满足条件,交换结束记得++i;--j; 
                               //!警告:必须++i;--j;否则一个数或者两个数的时候会死循环
        }                      //
    } while (i <= j);          //i<j 也是对的,可以通过
    
    //我们不妨考虑函数最后运行的结果
    //一定是i > mid > j
    //那么
    if (l<j) QuickSort(l,j);    //if的目的在于防止下标越界
    if (i<r) QuickSort(i,r);
}

void swap(int a, int b)        //这个函数功能交换,把数组array[a],array[b]里面的元素交换
{
    int temp = a;
    a = b;
    b = temp;
}

        这样就可以通过啦啊


>冒泡排序的解法 

        下面是完整的代码

#include <iostream>
using namespace std;

unsigned long long int array[1000005];
unsigned long long int n, t, k;
void QuickSort(int l, int r);
void swap(int a, int b);

int main()
{
    cin >> n >> t >> k;
    for (int i = 0; i < n; i++)
    {
        cin >> array[i];
    }
    
    QuickSort(0, n - 1);      //排序咯
    long long int temp = 0;
    long long int cnt = 0;
    k = k - t;                //提醒一下,这个笨蛋可能本身瞌睡值就很大几百万或者几亿,
                              //背着背着那不是更恐怖啦,所以减法一下下更机智
                              //不然后面加着加着可能会越界
    long long int i;
    for (i = 0; i < n; i++)
    {
        if (temp + array[i] >= k)    
        {
            break;
        }
        temp = temp + array[i];
    }                         //超出就停止
    cnt = i;
    int ans=(cnt*100+(n-cnt)*10)/n;
    cout<<ans;
    system("pause");
    return 0;
}


void QuickSort(int l, int r)
{
    int mid = (l + r) / 2;
    int i = l;
    int j = r;
    int x = array[mid];
    do
    {
        while(array[i]<x)
            ++i;             
        while(array[j]>x)
            --j;
        if (i <= j)
        {
            swap(array[i], array[j]);
            ++i;--j;
        }
        // cout << "#" << endl;
    } while (i <= j);
    if (l<j) 
        QuickSort(l,j);
    if (i<r) 
        QuickSort(i,r);
}

void swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

>补充知识:Sort函数的用法

        sort函数用于C++中,对给定区间所有元素进行排序,默认为升序,也可进行降序排序。sort函数进行排序的时间复杂度为n*log2n,比冒泡之类的排序算法效率要高,sort函数包含在头文件为#include<algorithm>的c++标准库中。

语法:

sort(start,end,cmp);

解释:

(1)start表示要排序数组的起始地址;

(2)end表示数组结束地址的下一位;

(3)cmp用于规定排序的方法,可不填,默认升序。

说明:

sort函数用于C++中,对给定区间所有元素进行排序,默认为升序,也可进行降序排序。

一般是直接对数组进行排序,例如对数组a[10]排序,sort(a,a+10)。而sort函数的强大之处在可与cmp函数结合使用,即排序方法的选择。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值