PDF文档公众号回复关键字:20240728
2020 CSP-J 完善程序2
1 完善程序 (单选题 ,每小题3分,共30分)
最小区间覆盖
给出 n 个区间,第 i 个区间的左右端点是 [ai,bi]。现在要在这些区间中选出若干个,使得区间 [0, m] 被所选区间的并覆盖(即每一个 0≤i≤m 都在某个所选的区间中)。保证答案存在,求所选区间个数的最小值。
输入第一行包含两个整数 n 和 m (1≤n≤5000,1≤m≤10^9) 接下来 n 行,每行两个整数 ai,bi
(0≤ai,bi≤m)
提示:使用贪心法解决这个问题。先用 O(n^2) 的时间复杂度排序,然后贪心选择这些区间
01 #include <iostream>
02 using namespace std;
03 const int MAXN = 5000;
04 int n, m;
05 struct segment { int a, b; } A[MAXN];
06
07 void sort() // 排序
08 {
09 for (int i = 0; i < n; i++)
10 for (int j = 1; j < n; j++)
11 if (①)
12 {
13 segment t = A[j];
14 ②
15 }
16 }
17
18 int main()
19 {
20 cin >> n >> m;
21 for (int i = 0; i < n; i++)
22 cin >> A[i].a >> A[i]?b;
23 sort();
24 int p = 1;
25 for (int i = 1; i < n; i++)
26 if (③)
27 A[p++] = A[i];
28 n = p;
29 int ans =0, r = 0;
30 int q = 0;
31 while (r < m)
32 {
33 while (④)
34 q++;
35 ⑤;
36 ans++;
37 }
38 cout << ans << endl;
39 return 0;
40 }
39.①处应填( )[3分]
A.A[j].b>A[j-1].b
B.A[j].a<A[j-1].a
C.A[j].a>A[j-1].a
D.A[j].b<A[j-1].b
40.②处应填( )[3分]
A.A[j+1]=A[j];A[j]=t;
B.A[j-1]=A[j];A[j]=t;
C.A[j]=A[j+1];A[j+1]=t;
D.A[j]=A[j-1];A[j-1]=t;
41.③处应填( )[3分]
A.A[i].b>A[p-1].b
B.A[i].b<A[i-1].b
C.A[i].b>A[i-1].b
D.A[i].b<A[p-1].b
42.④处应填( )[3分]
A.q+1<n&&A[q+1].a<=r
B.q+1<n&&A[q+1].b<=r
C.q<n&&A[q].a<=r
D.q<n&&A[q].b<=r
43.⑤处应填( )[3分]
A.r=max(r,A[q+1].b)
B.r=max(r,A[q].b)
C.r=max(r,A[q+1].a)
D.q++
2 相关知识点
1) 冒泡排序
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端
n个元素的冒泡排序,需要n趟完成,
每趟进行逐一两两比较,进行n-1次比较,把最大(最小)元素比较出来,交换到最后
2) 元素交换
int a=3;
int b=2;
int t;//通过t使a和b的值进行交换
t=a;
a=b;
b=t;
3) 贪心算法
所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择 。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的 局部最优解
例题
某商店不能电子支付,钱柜里的货币只有 25 分、10 分、5 分和 1 分四种硬币,如果你是售货员且要找给客户 41 分钱的硬币,如何安排才能找给客人的钱既正确且硬币的个数又最少?
分析
钱币保证最大面额大于其他小于它的面额之和,保证了每次选择最大的没有其他优于这个组合
即 选择了25 即使选择10+5+1=16都比25小
每次选择找零面额最大的,每次都是本次最优(局部最优),根据钱币面额本身的性质可知,可以保证全局最优
3 思路分析
1) 按左端点从小到大排序
07 void sort() // 排序
08 {
09 for (int i = 0; i < n; i++)
10 for (int j = 1; j < n; j++)
11 if (A[j].a<A[j-1].a)
12 {
13 segment t = A[j];
14 A[j]=A[j-1];A[j-1]=t;
15 }
16 }
2) 排除包含区间,会重复计算
去除1)图中的3线段
25 for (int i = 1; i < n; i++)
26 if (A[i].b>A[p-1].b)
27 A[p++] = A[i];
3) 去除几条线段组合重复区间
贪心选择最靠右边的线段,找到下一个区间左端点小于当前右端点的最后一个线段
33 while (q+1<n&&A[q+1].a<=r)
34 q++;
如下图1,2,4覆盖的区间和1,4覆盖的区间相同,需要去除线段2
39.①处应填( B )[3分]
A.A[j].b>A[j-1].b
B.A[j].a<A[j-1].a
C.A[j].a>A[j-1].a
D.A[j].b<A[j-1].b
分析
冒泡排线,比较相邻2个线段左端点的大小,如果后边小则交换,保证从小到大排序
40.②处应填( D )[3分]
A.A[j+1]=A[j];A[j]=t;
B.A[j-1]=A[j];A[j]=t;
C.A[j]=A[j+1];A[j+1]=t;
D.A[j]=A[j-1];A[j-1]=t;
分析
2个元素交换
41.③处应填( A )[3分]
A.A[i].b>A[p-1].b
B.A[i].b<A[i-1].b
C.A[i].b>A[i-1].b
D.A[i].b<A[p-1].b
分析
去除后面被包含的线段,不去除会重复计算线段
比如下面情况:1,2,4和1,4覆盖区间相同的,需要把2去除,否则计算覆盖线段数会多一个
42.④处应填( A )[3分]
A.q+1<n&&A[q+1].a<=r
B.q+1<n&&A[q+1].b<=r
C.q<n&&A[q].a<=r
D.q<n&&A[q].b<=r
分析
1)找下一个线段,下一个线段的左端点必须在上个线段右端点左边,保证区间是连续的
2)贪心选择最靠右边的线段,找到下一个区间左端点小于当前右端点的最后一个线段
43.⑤处应填( B )[3分]
A.r=max(r,A[q+1].b)
B.r=max(r,A[q].b)
C.r=max(r,A[q+1].a)
D.q++
分析
找到下一个线段,延长覆盖区间的长度,即延长覆盖区间的右端点