题目描述
注意:本题可以使用的头文件仅限于cstdio
iostream
cstring
梦回高三,小艾想起了几个月前背诵高考古诗文篇目的时光……
已经是晚上了。第二天有小测,可是小艾还有篇古诗文没有背。这篇古诗文都有一个对应的瞌睡值,也就是说,选择背诵篇目会让小艾的瞌睡程度增加。小艾目前的瞌睡程度已经是,而当小艾的瞌睡程度时,小艾会直接睡到明早,那可就没有更多的时间背了!注意,因为背了一半的文章也是没背出来的文章,所以恰好让小艾瞌睡程度的那篇也视作没背的。
好在小测考到每个篇目的概率是一致的,所以为了小测分数更高,小艾只需要背诵尽可能多的文章即可。小艾预计,自己背了的篇目明日小测一定能默写对,没背的篇目有10%的概率瞎写对。小测只考察这n篇古诗词默写,每篇各一题,每题分值相同,小测满分为100。
“夜如何其?夜未艾。”时间不早了,小艾对明天的小测还有些紧张。请帮助小艾计算熬夜背书后,明日的小测期望最多能得多少分吧!
输入格式
输入共两行。
第1行为三个整数分别表示篇目数,小艾现在的瞌睡值,以及让小艾直接睡着的瞌睡值。
第2行为个非负整数,表示这篇古诗文的瞌睡值。
输出格式
输出共一行。
第1行为一个整数,表示小艾的期望最大分数(向下取整)。
样例输入
5 0 10
2 4 6 10 1
样例输出
64
共5道题,故每题20分。小艾最多能背诵三篇文章(瞌睡值为2 4 1),期望得分为
数据范围
对于40%的数据,
对于另外20%的数据,
对于再另外40%的数据,
对于全部数据,还有
题目吐槽
首先这个人可真是个人才,要背那么多篇古文还不早点开始背,而且妄想一晚上背完全部……平时不好好学习。。。
是不是像极了期末考试前的你………………
最离谱的是,看完那个数据………………
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
是的,我确实很无语。。。
乖乖,不至于吧,这古文量好像有点大呢,那个我们姑且不评论,那估计是个卷王,就那个5000篇,也是够离谱啦吧,他真的背的完吗
突然又想起sjtu的英语小测 嘶………………学不完了学不完了
幸亏我下学期才学数据结构
还是去睡觉吧,睡觉多好多香啊
还有也不知道为啥这个题目名字叫麦当劳,莫非出题者是在麦当劳配的数据?突然香气来了2333
只允许cstdio
iostream
cstring,似乎目的是不允许你用sort函数,那就自己二分吧
二分法yyds
不吐槽了,总之就是肥肠离谱,还是看题目吧。
题目解答
非常明显,这个题目就是四个步骤
- 把这些奇奇怪怪的古文按照瞌睡程度排个序o(╥﹏╥)o
- 从小到大开始背书吧 o(╥﹏╥)o,就像在sjtu英语小测前刷英语课文一样,当然选短的优先看
- 背不会那就去睡觉 ヾ(✿゚▽゚)ノ好耶,(有几次我都没看完还是躺平啦)
- 考多少那就得看运气啦()
sort函数不让用可还行,使用方法在文末。
似乎这个题目很适合学习一手排序方法,
还是先看看最普通的方法吧冒泡排序:
不妨先复习一下冒泡的原理吧,如果会请直接忽略下面部分:
>补充知识:冒泡排序的原理
首先我们假设有一个数组(至于是从0还是从1开始取决于自己),第一行是数值,第二行是对应数列的编号
编号 | |||||||
数值 | 8 | 2 | 5 | 7 | 1 | 9 | 0 |
要想完成排序则需要经过起泡这个过程
第一轮起泡:固定左边一个位置(绿色部分),从左往右扫描黄色部分的数字
编号 | |||||||
数值 | 8 | 2 | 5 | 7 | 1 | 9 | 0 |
一旦发现比绿色部分元素小的数字,立即执行交换:例如就需要发生交换,交换后如下图
编号 | |||||||
数值 | 2 | 8 | 5 | 7 | 1 | 9 | 0 |
继续扫描,下一个要交换的是
编号 | |||||||
数值 | 1 | 8 | 5 | 7 | 2 | 9 | 0 |
继续扫描,下一个要交换的是
编号 | |||||||
数值 | 0 | 8 | 5 | 7 | 2 | 9 | 1 |
扫描完成,第一个位置已经确认,也是所有数组里面最小的数字,已经确认的用蓝色标识,然后绿色标识右移,开始第二轮起泡
编号 | |||||||
数值 | 0 | 8 | 5 | 7 | 2 | 9 | 1 |
第二轮气泡类似第一轮,可以发现,第轮气泡之后,就会确认,这样下去,总共7轮即可完成排序
思考:对于有n项的数列需要多少回合?最多需要次,所以时间复杂度为。
>冒泡排序的解法
同理,用冒泡排序法解决问题的代码如下:
#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的数列
编号 | ||||||||||
数值 | 5 | 7 | 3 | 0 | 4 | 2 | 1 | 9 | 6 | 8 |
1.首先我们从这10个数字里面随意取一个,如,并将其保存到变量k中,变量k将会作为一个参考值用于后续的比较
为了便于理解,我们不妨先把清空,实际也可以不清空
编号 | ||||||||||
数值 | 7 | 3 | 0 | 4 | 2 | 1 | 9 | 6 | 8 |
2.定义low与high两个变量,low=1,high=10;high准备从右往左开始扫描(参照对象为 )
编号 | ||||||||||
数值 | 7 | 3 | 0 | 4 | 2 | 1 | 9 | 6 | 8 |
3.high从10开始往左扫描,只要大于或等于k就pass,小于k就停止,把这个元素放到a[low];扫描过的用黄色表示
编号 | ||||||||||
数值 | 7 | 3 | 0 | 4 | 2 | 1 | 9 | 6 | 8 |
high从右往左扫描,现在我们发现 ,那么就应该放在,low继续扫描
编号 | ||||||||||
数值 | 1 | 7 | 3 | 0 | 4 | 2 | 9 | 6 | 8 |
low从左往右扫描,又发现比k大,所以调位置放在右边去啦
编号 | ||||||||||
数值 | 1 | 3 | 0 | 4 | 2 | 7 | 9 | 6 | 8 |
high继续从右往左扫描,又发现比k小,放左边去啦
编号 | ||||||||||
数值 | 1 | 2 | 3 | 0 | 4 | 7 | 9 | 6 | 8 |
low继续从左往右扫描,发现 ,呵呵哈哈哈low一路疯狂自增到
编号 | ||||||||||
数值 | 1 | 2 | 3 | 0 | 4 | 7 | 9 | 6 | 8 |
好啦low与high回合啦好耶ヾ(✿゚▽゚)ノ,在把k放到会和的a6里面去
编号 | ||||||||||
数值 | 1 | 2 | 3 | 0 | 4 | 5 | 7 | 9 | 6 | 8 |
聪明的你一定已经发现的位置已经是对哒啦,现在要排序~,~
递归即可啦
这个函数是负责排序的总函数
//排序函数,得到从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函数结合使用,即排序方法的选择。