1. 程式人生 > >五一培訓 清北學堂 DAY1

五一培訓 清北學堂 DAY1

沒有 max 區間 擴展 int 數據 span mes 好的

今天是馮哲老師的講授~

1.枚舉

枚舉也稱作窮舉,指的是從問題所有可能的解的集合中一一枚舉各元素

用題目中給定的檢驗條件判定哪些是無用的,哪些是有用的。能使命題成立的即為其解。

例一
一棵蘋果樹上有n個蘋果,每個蘋果長在高度為Ai的地方。
小明的身高為x
他想知道他最多能摘到多少蘋果
數據範圍: 1 n, Ai, x 100

題解
問題相當於詢問有多少i滿足Ai <= x
考慮用for循環枚舉每一個蘋果是否能被摘到即可。
技術分享圖片

例二
判斷一個數X是否是素數
1 X 109
題解
考慮定義,若X為合數,則必然有:
1 < i < X, 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(NN),不符合要求
篩法求素數
仍然考慮枚舉判斷每個數是否是素數,但我們這次從2開始判斷。

考慮對於任意一個數x,不論x是否為素數,都
x*2,x*3,x*4...為合數。我們“篩”掉這些必然為合數的數。
那麽當我們枚舉到ii還沒有被篩掉,那麽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(n0) 11..11(n1)
對於任意一種中間狀態,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(NN + NlogN
)

策略二
預處理出區間所有素數,枚舉素數判定是否是回文數
時間復雜度O(NlogN) ,判斷回文數的時間復雜度可以不計。

策略三
枚舉區間內所有回文數,判斷是否是素數
枚舉回文數即枚舉一個數的前一半,再手動擴展成完整的數
另外,偶數位數的回文數都必然是11的倍數,不需要枚舉。
時間復雜度O(√N * √N) = O(N),第一個√N是只要枚舉前一半就好了,第二個√N是判素數。

枚舉法的優缺點:

優點:
簡單明了,分析直觀
能夠幫助我們更好地理解問題
運用良好的枚舉技巧可以使問題變得更簡單

缺點:
時空間效率低
往往沒有利用題目中的特殊性質
產生了大量冗余狀態


五一培訓 清北學堂 DAY1