資訊學奧賽初賽天天練-81-NOIP2015普及組-完善程式-二分答案、二分查詢、中位數、二分邊界、二分時間複雜度

new-code發表於2024-09-01

1 完善程式 (單選題 ,每小題3分,共30分)

中位數 median

給定 n(n為奇數且小於 1000)個整數,整數的範圍在 0∼m(0<m<2^31) 之間,請使用二分法求這 n個整數的中位數。所謂中位數,是指將這 n個數排序之後,排在正中間的數。(第五空 2 分,其餘 3 分)

01 #include <iostream>
02 using namespace std;
03 
04 const int MAXN = 1000;
05 int n, i, lbound, rbound, mid, m, count;
06 int x[MAXN];
07 
08 int main()
09 {
10     cin >> n >> m;
11     for (i = 0; i < n; i++)
12         cin >> x[i];
13     lbound = 0;
14     rbound = m;
15     while (①)
16     {
17         mid = (lbound + rbound) / 2;
18         ②;
19         for (i = 0; i < n; i++)
20             if (③)
21                 ④;
22         if (count > n / 2)
23             lbound = mid + 1;
24         else
25             ⑤;
26         cout << mid << " " << lbound << " " << rbound << " " << count << endl;
27     }
28     cout << rbound << endl;
29     return (0);
30 }

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)

3 思路分析

1 在0~m範圍內,二分列舉,把原來n個數分成2部分

2 統計其中一部分的個數,本題統計大於mid數的個數,計入變數count

3 如果count>n/2 ,即count大於n的一半,向右縮小範圍,下次計算讓count變小

4 否則 如果count<=n/2 ,向左縮小範圍,下次計算讓count變大一些

5 上面3和4步驟的目標是讓count=n/2,找到最中間的數

1 ①處應填( lbound < rbound )

分析

根據
//左閉右閉 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;

此處依賴第5題
由於lbound = mid + 1; 是左閉區間
由於第5題填rbound=mid; 是右開區間,所以 lbound  < rbound 
第5題填rbound=mid;的原因,請參考第5題解析
22         if (count > n / 2)
23             lbound = mid + 1;
24         else
25             ⑤;

2 ②處應填( count=0 )

分析

每次二分後,需要重新計算大於mid的數的個數,所以需要先對count初始0
不初始0的話,上次二分的計算結果會對本次產生影響,count值不對

3 ③處應填( x[i]>mid )

分析

迴圈統計大於mid的數個數,如果x[i]>mid ,count累加,count++
19         for (i = 0; i < n; i++)
20             if (③)
21                 ④;

4 ④處應填( count++ )

分析

參考第3填

5 ⑤處應填( rbound )

分析

由於輸出結果是rbound,count=n/2在rbound這裡賦值
rbound=mid;此時計算出來mid是要找的中位數,如果rbound=mid-1的話,rbound會比實際答案小1
22         if (count > n / 2)
23             lbound = mid + 1;
24         else
25             ⑤;

28     cout << rbound << endl;

相關文章