五一培訓 清北學堂 DAY1
今天是馮哲老師的講授~
1.枚舉
枚舉也稱作窮舉,指的是從問題所有可能的解的集合中一一枚舉各元素。
用題目中給定的檢驗條件判定哪些是無用的,哪些是有用的。能使命題成立的即為其解。
例一
一棵蘋果樹上有n個蘋果,每個蘋果長在高度為Ai的地方。
小明的身高為x
他想知道他最多能摘到多少蘋果
數據範圍: 1 ≤ n, Ai, x ≤ 100
題解
問題相當於詢問有多少i滿足Ai <= x
考慮用for循環枚舉每一個蘋果是否能被摘到即可。
例二
判斷一個數X是否是素數
1 ≤ X ≤ 109
題解
考慮定義,若X為合數,則必然有:
∃1 < i < X, i|X
時間復雜度O(N),不符合要求;
事實上我們發現,假設X是一個合數,那麽必然有:
X = a * b,必然有:
min(a, b) <= √X
因此我們枚舉的範圍可以從X變為√X
時間復雜度O(√N)
例三
求[l, r]這段區間中有多少素數
1 ≤ l ≤ r ≤ 10^6
題解
一個顯然的想法是利用for循環枚舉[l, r]中的每一個數。然後
利用例二中的知識O(√X)進行判斷。
整體復雜度O(N√N),不符合要求
篩法求素數
仍然考慮枚舉判斷每個數是否是素數,但我們這次從2開始判斷。
考慮對於任意一個數x,不論x是否為素數,都
有x*2,x*3,x*4...為合數。我們“篩”掉這些必然為合數的數。
那麽當我們枚舉到i,i還沒有被篩掉,那麽i必然為素數。
時間復雜度O(NlogN)
也就是埃拉托斯特尼篩法,簡稱埃氏篩
具體為什麽不用歐拉篩(線性篩),我也不知道,老師說先不講……
例四
T次詢問,每次詢問[l, r]中有多少素數
1 ≤ T ≤ 105, 1 ≤ l ≤ r ≤ 10^6
題解
有多次詢問,如果每次詢問我們都先找一遍,那麽肯定TLE,那麽咋辦呢?
我們用ANSL,R來表示[l, r]中有多少素數
運用前綴和的思想(先進行預處理):
#include<cstdio> using namespace std; #define MAXN 1000005 int sum[MAXN],vis[MAXN]; //sum儲存1~i的質數個數,vis記錄第i個數是否為質數 int t,l,r; void pre() //預處理所有1~MAXN內的質數個數 { for(int i=2;i<=MAXN;i++) { sum[i]=sum[i-1]; //1到i的質數個數一定大於等於i到i-1的質數個數,所以利用前綴和求質數個數 if(!vis[i]) sum[i]++; //如果i是質數,個數加一 for(int j=i*2;j<=MAXN;j+=i) //埃氏篩把i所有的倍數都判為合數 vis[j]=1; //標記為合數 } } int main() { pre(); scanf("%d",&t); while(t--) { scanf("%d%d",&l,&r); printf("%d\n",sum[r]-sum[l-1]); //直接利用前綴和求質數個數 } return 0; }
例五
已知n個整數x1, x2, .., xn,以及一個整數k,k < n。從n個數字
中任選k 個整數相加,可分別得到一系列的和。
例如當n = 4, k = 3,四個整數分別為3,7,12,19時,可得全部
的組合與他們的和為:
3 + 7 + 12 = 22
3 + 7 + 19 = 29
7 + 12 + 19 = 38
3 + 12 + 19 = 34
現在,要求計算出和為素數的組合共有多少種。
例如上例,只有一種組合的和為素數:3 + 7 + 19 = 29
1 ≤ n ≤ 20, k < n
1 ≤ x1, x2, .., xn ≤ 5 * 106
題解
首先我們來考慮如何枚舉這樣的組合。
我們用ai來表示第i個數是否被選
ai = 1表示這個數被選擇了
ai = 0表示這個數未被選擇
枚舉過程相當於枚舉了一組二進制狀態
比如對於五個數1,2,3,4,5
01010表示我們選擇了2,4,未選擇1,3,5
在不考慮k的限制的情況下,我們枚舉所有組合就相當於
枚舉00..00(n個0) → 11..11(n個1)
對於任意一種中間狀態,0的個數+1的個數為n
我們假設這是一個長為n的二進制數,我們將它轉換成十進制。因為十進制的0(每個都不選)到2^n(每個數都選)中的每個數就是對應著其中的每一情況
事實上就是枚舉了一個數,範圍是[0, 2^n)
判斷位置i是否為1使用位運算來完成
看二進制中1的個數是否等於k,如果是就進一步判斷是否和為素數。Check部分即為判斷是否為素數。
考慮到最大Sum不超過20 * 500w,預處理出10000以內的素數可以加速。
例六
求[l,r]中有多少數既是回文數又是素數
1 ≤ l ≤ r ≤ 10
策略一
枚舉每個數,判斷他是不是回文數,判斷他是不是素數
時間復雜度O(N√N + NlogN)
策略二
預處理出區間所有素數,枚舉素數判定是否是回文數
時間復雜度O(NlogN) ,判斷回文數的時間復雜度可以不計。
策略三
枚舉區間內所有回文數,判斷是否是素數
枚舉回文數即枚舉一個數的前一半,再手動擴展成完整的數
另外,偶數位數的回文數都必然是11的倍數,不需要枚舉。
時間復雜度O(√N * √N) = O(N),第一個√N是只要枚舉前一半就好了,第二個√N是判素數。
枚舉法的優缺點:
優點:
簡單明了,分析直觀
能夠幫助我們更好地理解問題
運用良好的枚舉技巧可以使問題變得更簡單
缺點:
時空間效率低
往往沒有利用題目中的特殊性質
產生了大量冗余狀態
五一培訓 清北學堂 DAY1