信息学奥赛初赛天天练-75-NOIP2016普及组-完善程序-二分答案、二分查找、贪心算法、贪心策略
1 完善程序 (单选题 ,每小题3分,共30分)
郊游活动
有 n名同学参加学校组织的郊游活动,已知学校给这 n名同学的郊游总经费为 A元,与此同时第 i位同学自己携带了 Mi 元。为了方便郊游,活动地点提供 B(≥n) 辆自行车供人租用,租用第 j辆自行车的价格为 Cj元,每位同学可以使用自己携带的钱或者学校的郊游经费,为了方便账务管理,每位同学只能为自己租用自行车,且不会借钱给他人,他们想知道最多有多少位同学能够租用到自行车。(第四、五空 2.5 分,其余 3分)
本题采用二分法。对于区间 [l,r],我们取中间点 mid 并判断租用到自行车的人数能否达到 mid。判断的过程是利用贪心算法实现的。
源程序
#include <iostream>
using namespace std;
01 #define MAXN 1000000
02
03 int n, B, A, M[MAXN], C[MAXN], l, r, ans, mid;
04
05 bool check(int nn) {
06 int count = 0, i, j;
07 i = ①;
08 j = 1;
09 while (i <= n) {
10 if(②)
11 count += C[j] - M[i];
12 i++;
13 j++;
14 }
15 return ③;
16 }
17
18 void sort(int a[], int l, int r) {
19 int i = l, j = r, x = a[(l + r) / 2], y;
20 while (i <= j) {
21 while (a[i] < x) i++;
22 while (a[j] > x) j--;
23 if (i <= j) {
24 y = a[i]; a[i] = a[j]; a[j] = y;
25 i++; j--;
26 }
27 }
28 if (i < r) sort(a, i, r);
29 if (l < j) sort(a, l, j);
30 }
31
32 int main() {
33 int i;
34 cin >> n >> B >> A;
35 for (i = 1; i <= n; i++)
36 cin >> M[i];
37 for (i = 1; i <= B; i++)
38 cin >> C[i];
39 sort(M, 1, n);
40 sort(C, 1, B);
41 l = 0;
42 r = n;
43 while (l <= r) {
44 mid = (l + r) / 2;
45 if(④){
46 ans = mid;
47 l = mid + 1;
48 }else
49 r = ⑤;
50 }
51 cout << ans << endl;
52 return 0;
53 }
1 ①处应填( )
2 ②处应填( )
3 ③处应填( )
4 ④处应填( )
5 ⑤处应填( )
2 相关知识点
二分答案
二分答案顾名思义,它用二分的方法枚举答案,并且枚举时判断这个答案是否可行
直接对答案进行枚举查找,接着判断答案是否合法。如果合法,就将答案二分进一步靠近,如果不合法,就接着二分缩小判断。这样就可以大大的减少时间。
二分中有时可以得可行得答案,但不是最大的,继续向右靠近,求出最大值
int ans = 1;
int l = 1,r = 100000;//在1~100000之间的整数枚举
while(l <= r){
int m = l + (r - l) / 2;
if(check(m)){//满足 则进行向右缩小范围 看看有没有更大的
ans = m;//可能多次赋值 最后一定是可能的最大值
l = m + 1;
}else{//不满足缩小边长 向左缩小范围 用更小边长继续尝试
r = m - 1;
}
}
二分找边界
//左闭右闭 while left right 最终left=right+1
while(left<=right) left = mid + 1; right =mid-1;
//左闭右开 while left right 最终left=right
while(left<right) left = mid + 1; right =mid;
//左开右闭 while left right 最终left=right
while(left<right) left=mid; right=mid-1;
//左开右开 while left right 最终left=right-1
while(left+1<right) left=mid; right=mid;
二分查找时间复杂度
二分查找每次都缩小或扩大为原来的一半,所以也是Olog(n)
贪心算法
所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择 。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的 局部最优解
例题
某商店不能电子支付,钱柜里的货币只有 25 分、10 分、5 分和 1 分四种硬币,如果你是售货员且要找给客户 41 分钱的硬币,如何安排才能找给客人的钱既正确且硬币的个数又最少?
分析
钱币保证最大面额大于其他小于它的面额之和,保证了每次选择最大的没有其他优于这个组合
即 选择了25 即使选择10+5+1=16都比25小
每次选择找零面额最大的,每次都是本次最优(局部最优),根据钱币面额本身的性质可知,可以保证全局最优
3 思路分析
求最多有多少位同学能够租用到自行车,总共n位同学,1~n 位同学之间二分的方法判定是否可以能租
判定是否可以租,使用贪心算法,具体如下
贪心策略,较多钱的同学去租相对便宜的车
//判定nn位同学是否可以租到自行车
05 bool check(int nn) {
06 int count = 0, i, j;
07 i = ①;//n-nn+1 取钱较多的nn个人 贪心算法
08 j = 1;//租车费用从小到大租,贪心算法
09 while (i <= n) {
10 if(②)//M[i]<C[j] 如果当前同学带的钱不够,累加起来,看看学校经费是否够
11 count += C[j] - M[i];
12 i++;
13 j++;
14 }
15 return ③;//count<=A 看学校经费是否够 ,经费够可以租nn辆车,经费不够不能租nn辆车
16 }
1 ①处应填( n-nn+1 )
分析
07 i = ①;//n-nn+1 取钱较多的nn个人 贪心算法
例如
n=10 nn=6
从10-6+1=5,到10这6个人
因为前面按从小到大进行了排序
5~10这6人,比1~6这6人有钱
2 ②处应填( M[i]<C[j] )
分析
10 if(②)//M[i]<C[j] 如果当前同学带的钱不够,累加起来,看看学校经费是否够
如果钱带的够,就可以租n辆车
为了让钱不够的同学租到车,需要从学校经费借钱,把借的钱累加起来,后续判定是否超出经费
3 ③处应填( count<=A )
分析
15 return ③;//count<=A 看学校经费是否够 ,经费够可以租nn辆车,经费不够不能租nn辆车
4 ④处应填( check(mid) )
分析
/*二分思想,计算左右边界的中间租车数量,如果可以组,看看能否可以租更多,如果不能租,看看少租一些车是否可行
mid = (l + r) / 2;
check(mid)
*/
43 while (l <= r) {
44 mid = (l + r) / 2;
45 if(④){
46 ans = mid;
47 l = mid + 1;
48 }else
49 r = ⑤;
50 }
5 ⑤处应填( mid-1 )
分析
二分边界,如下为左右闭区间,l=mid+1,r=mid-1
所以填mid-1
43 while (l <= r) {
作者:newcode 更多资源请关注纽扣编程微信公众号
从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习