2023 CSP-S 閱讀程式2
判斷題正確填 √,錯誤填 ⨉ ;除特殊說明外,判斷題 1.5 分,選擇題 3 分,共計 40 分)
01 #include <vector>
02 #include <algorithm>
03 #include <iostream>
04
05 using namespace std;
06
07 bool f0(vector<int> &a, int m, int k) {
08 int s = 0;
09 for (int i = 0, j = 0; i < a.size(); i++) {
10 while (a[i] - a[j] > m) j++;
11 s += i - j;
12 }
13 return s >= k;
14 }
15
16 int f(vector<int> &a, int k) {
17 sort(a.begin(), a.end());
18
19 int g = 0;
20 int h = a.back() - a[0];
21 while (g < h) {
22 int m = g + (h - g) / 2;
23 if (f0(a, m, k)) {
24 h = m;
25 }
26 else {
27 g = m + 1;
28 }
29 }
30
31 return g;
32 }
33
34 int main() {
35 int n, k;
36 cin >> n >> k;
37 vector<int> a(n, 0);
38 for (int i = 0; i < n; i++) {
39 cin >> a[i];
40 }
41 cout << f(a, k) << endl;
42 return 0;
43 }
假設輸入總是合法的且 1≤ai≤10^8,n≤10000,1≤k≤n(n−1)/2,完成下面的判斷題和單選題
判斷題
1 將第 24 行的 m
改為 m - 1
,輸出有可能不變,而剩下情況為少 1( )
2 將第 22 行的 g + (h - g) / 2
改為 (h + g) >> 1
,輸出不變( )
3 當輸入為 5 7 2 -4 5 1 -3
,輸出為 5
( )
單選題
4 設 a陣列中最大值減最小值加 1 為 A,則 f
函式的時間複雜度為( )
A O(nlogA)
B O(n^2logA)
C O(nlog(nA))
D O(nlogn)
5 將第 10 行中的 >
替換為 >=
,那麼原輸出與現輸出的大小關係為( )
A 一定小於
B 一定小於等於且不一定小於
C 一定大於等於且不一定大於
D 以上三種情況都不對
6 當輸入為 5 8 2 -5 3 8 -12
,輸出為( )
A 13
B 14
C 8
D 15
2 相關知識點
1) 對數求和
同底的兩個對數相加,底數不變真數相乘
例題
log4=2
log8=3
log4 + log8 = log(4*9)=log32=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;
}
}
二分查詢中間值
/* 向右逼近,如果找到滿足條件的數,會繼續向右找更大的數,讓迴圈結束
mid=(left+right)/2 left和right都接近最大值時,可能溢位可以使用下面寫法替換
mid=left + (right-left) / 2;
可以求滿足條件的最大值
*/
/* 向左逼近,如果找到滿足條件的數,會繼續向左找更小的數,讓迴圈結束
mid=(left+right+1)/2 left和right都接近最大值時,可能溢位可以使用下面寫法替換
mid=left + (right-left+1) / 2;
可以求滿足條件的最小值
*/
二分找邊界
//左閉右閉 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)
3 思路分析
假設輸入總是合法的且 1≤ai≤10^8,n≤10000,1≤k≤n(n−1)/2,完成下面的判斷題和單選題
判斷題
1 將第 24 行的 m
改為 m - 1
,輸出有可能不變,而剩下情況為少 1( T )
分析
1在二分時,如果是從g=m+1找到答案的,則結果不變 - m時 不是答案 走 else m+1 為答案時
2在二分時,如果找到m為最小答案,h=m-1 ,實際結果會比答案少1
如果想獲取正確結果,結束條件 while (g<=h) g=h+1 即g=m+1 (把前h=m-1再加回來)
根據上述1和2,輸出結果可能不變,或者比實際少1,因此正確
21 while (g < h) {
22 int m = g + (h - g) / 2;
23 if (f0(a, m, k)) {
24 h = m;
25 }
26 else {
27 g = m + 1;
28 }
2 將第 22 行的 g + (h - g) / 2
改為 (h + g) >> 1
,輸出不變( T )
分析
g + (h - g) / 2 和 h + g) >> 1
上述取中間數的寫法主要區別為第1種會避免一個數接近資料型別的範圍時,兩個數相加避免溢位
由於輸入的1≤ai≤10^8,所以(h+g)最大2*10^8 小於整形的最大取值範圍,2*10^10
所以上述改變不影響輸出
3 當輸入為 5 7 2 -4 5 1 -3
,輸出為 5
( T )
分析
5 7
對輸入的數進行排序
-4 -3 1 2 5
二分模擬
g=0,h=5-(-4)=9 m=(0+9)/2=4
-3-(-4)=1,1-(-3)=4,2-1=1,5-2=3,5-1=4 共4對,少於7,需要增大範圍
g=5,h=9 m=(5+9)/2=7
-3-(-4)=1,1-(-3)=4,2-1=1,5-2=3, 1-(-4)=5, 2-(-4)=6,2-(-4)=6,2-(-3)=5,5-(1)=4 共9對,大於7,縮小範圍,看是否有更小的滿足
g=5,h=7 m=(5+7)/2=6
-3-(-4)=1,1-(-3)=4,2-1=1,5-2=3, 1-(-4)=5, 2-(-4)=6,2-(-4)=6,2-(-3)=5,5-(1)=4 共9對,大於7,縮小範圍,看是否有更小的滿足
g=5,h=6 m=(5+6)/2=5
-3-(-4)=1,1-(-3)=4,2-1=1,5-2=3, 1-(-4)=5, 2-(-4)=6,2-(-3)=5,5-(1)=4 共8對,大於7,此時g=h 退出迴圈
所以輸出5
單選題
4 設 a陣列中最大值減最小值加 1 為 A,則 f
函式的時間複雜度為( C )
A O(nlogA)
B O(n^2logA)
C O(nlog(nA))
D O(nlogn)
分析
17 sort(a.begin(), a.end());
sort排序,時間複雜度為O(n*logn)
二分為logA,每次呼叫f0函式,為n,所以時間複雜度為n*logA
n*logn +n*logA = n(logn+logA)=nlog(nA)
所以選C
21 while (g < h) {
22 int m = g + (h - g) / 2;
23 if (f0(a, m, k)) {
24 h = m;
25 }
26 else {
27 g = m + 1;
28 }
29 }
07 bool f0(vector<int> &a, int m, int k) {
08 int s = 0;
09 for (int i = 0, j = 0; i < a.size(); i++) {
10 while (a[i] - a[j] > m) j++;
11 s += i - j;
12 }
13 return s >= k;
14 }
5 將第 10 行中的 >
替換為 >=
,那麼原輸出與現輸出的大小關係為( B )
A 一定小於
B 一定小於等於且不一定小於
C 一定大於等於且不一定大於
D 以上三種情況都不對
分析
while (a[i] - a[j] > m) j++; 替換為while (a[i] - a[j] >= m) j++;
等於m的時候不被統計數量,如果正好有這個邊界值,可能f0可能變成false,不能找到更小的區間,輸出變大
如果沒有這個邊界值,對結果沒有影響
所以原輸出值一定小於等於現輸出值
6 當輸入為 5 8 2 -5 3 8 -12
,輸出為( B )
A 13
B 14
C 8
D 15
分析
5 8
對輸入的數進行排序
-12 -5 2 3 8
二分模擬
g=0,h=8-(-12)=20 m=(0+20)/2=12
-5-(-12)=7,2-(-5)=7,3-2=1,8-3=5, 3-(-5)=8,8-2=6共6對,少於8,需要增大範圍
g=13,h=20 m=(13+20)/2=16
-5-(-12)=7,2-(-5)=7,3-2=1,8-3=5, 2-(-12)=14,3-(-12)=15,3-(-5)=8,8-(-5)=13,8-2=6共9對,大於8,需要縮小範圍
g=13,h=16 m=(13+16)/2=14
-5-(-12)=7,2-(-5)=7,3-2=1,8-3=5, 2-(-12)=14,3-(-5)=8,8-(-5)=13,8-2=6共8對,大於等於8,需要縮小範圍
g=13,h=14 m=(13+16)/2=13
-5-(-12)=7,2-(-5)=7,3-2=1,8-3=5,3-(-5)=8,8-(-5)=13,8-2=6共8對,小於8,此時g=13,h=14,g+1後g=h退出迴圈
返回g+1=14
所以選B