演算法入門經典第八章學習筆記(上)

2021-06-20 11:20:44 字數 3789 閱讀 4664

【教學內容相關章節】

8.1演算法分析初步     8.2再談排序與搜尋         8.3遞迴與分治  8.4貪心法     

【教學目標】 (加粗表示基本掌握)

(1)理解「基本操作」、漸近時間複雜度的概念和大o記號的含義;

(2)掌握「最大連續和」問題的各種演算法及其時間複雜度分析;

(3)正確認識演算法分析的優點和侷限性,能正確使用分析結果;

(4)掌握歸併排序和逆序對統計的分治演算法;

(5)掌握歸併排序和快速選擇演算法;

(6)熟練掌握二分查詢演算法,包括找上下界的演算法;

(7)能用遞迴的方式思考和求解問題; 

(8)熟練掌握用二分法求解非線性方程的方法; 

(9)熟練掌握用二分法把優化問題轉化為判定問題的方法; 

(10)熟悉能用貪心法求解的各類經典的問題。

例8-1:最大連續和(注:陣列下標從1開始)

(1)列舉的思想。計算次數用數學計算為n*(n+1)*(n+2)/6,時間複雜度o(n^n^n)。

best = a[1];

for (i = 1; i <= n; i++)

}

(2)優化,設si=a1+a2+„+ai,則ai+ai+1+„+aj=sj-si-1,它的含義是連續子串行之和等於兩個字首和之差。同樣可計算出計算次數為 n*(n+1)/2,時間複雜度為o(n^n)。

s[0] = 0;

for (i = 1; i <= n; i++)

s[i] = s[i-1] + a[i];

for (i = 1; i <= n; i++)

for (j = i; j <= n; j++)

if (s[j] - s[i-1] > best) best = s[j] - s[i-1];

(3)分治法:

分治法一般分為3個步驟:

1、劃分問題,把問題的例項劃分成若干子問題;

2、遞迴問題,遞迴解決子問題;

3、合併問題,合併子問題的解得到原問題的解。

本例中,「劃分」就是把序列分成元素個數盡量相等的兩半;「遞迴求解」就是分別求出完全位於左半或完全位於右半的最佳序列;「合併」就是求出起點位於左半、終點位於右半的最大連續和序列,並和子問題的最優解比較。

貼上乙個完整能跑的程式。注意區間為左閉右開[x,y)。遞迴方程t(n)=2t(n/2)+n,t(1)=1的解為t(n)=θ(nlogn)

#include #include #include #include #include #include #include #include #include using namespace std;

int maxsum(int *a, int x, int y)

v = 0; r = a[m];

for (i = m; i < y; i++)

if (l+r > max) max = l+r;

}int main()

(4)(沒怎麼想)再優,把o(n2)演算法稍作修改,便可以得到乙個o(n)演算法:當j確定時,「s[j]-s[i-1]最大」相當於「s[i-1]最小」,因此只需要掃瞄一次陣列,維護「目前遇到過的最小s」即可。

歸併排序也是按照分治法的三步,劃分、遞迴和合併。關鍵在於第三步,每次只需要把兩個序列的最小元素加以比較,刪除其中的較小元素並加入合併後的新錶即可。由於需要乙個新表來存放結果,所以附加空間n。

#include #include #include #include #include #include #include #include #include using namespace std;

void merge_sort(int *a, int x, int y, int *t)

for (int i = x; i < y; i++)

a[i] = t[i]; }}

int main()

函式判斷中用的'||"挺實用,因為滿足第乙個就不會去判斷第二個。

例8-2 逆序對數。

給一列a1,a2,„,an,求它的逆序對數,即有多少個有序對(i,j),使得iaj。n可以高達10^6。

和歸併排序一樣,例如 4 3 2 1,剛開始分為 4 3 | 2 1 ,兩邊都有乙個逆序對,然後變為 3 4| 1 2,此時如果左邊的比右邊的大就加上 m-p個。所以2+(2+2) = 6.

else

僅是這裡不同和函式引數不同。

按照分治三步法,將快速排序演算法作如下介紹。 

(1)劃分問題:陣列的各個元素重排後分成左右兩部分,使得左邊的任意元素都小於或等於右邊的任意元素。 

(2)遞迴問題:把左右兩部分分別排序; 

(3)合併問題:例8-3 第k小的數。不用合併,因為此時陣列已經完全有序。

例8-3 第k小的數。

輸入n個整數和乙個正整數(1≤k≤n),輸入這些整數從小到大排序後的第k個(例如,k=1就是最小值)。n≤107。

這裡的快排和資料結構上學的基本一樣,原理就是把第乙個元素作為數軸,交換後邊的的,最後使數軸左右兩邊是比他大和小的。但是這個題目有些地方沒懂。

例如 int pos = q - low + 1; 和 return select_k(a, q+1, high, k-pos);中的k-pos。這裡先放下,希望大牛路過指點。

#include #include #include #include #include #include #include #include #include using namespace std;

const int n = 100;

int partition(int a[n], int low, int high)

a[low] = a[j];

a[j] = x;

return j;

}int select_k(int a[n], int low, int high, int k)

int main()

return -1;

}int main()

; cout << binsearch(a, 0, 10, 5) << endl;

}

二分查詢的乙個缺陷是如果和查詢值相等的值有若干個,則可能返回的位置不是我們想要的位置——例如返回第乙個的位置,並且如果不存在的時候,也返回乙個位置,是的要查詢的值插在這個位置上後仍然有序。

(二分查詢求下界)

int binsearch(int *a, int x, int y, int v)

return x;

}

類似可以寫出二分查詢求上界。

例8-4 範圍統計。

給出n個整數xi和m個詢問,對於每個詢問(a,b),輸出閉區間[a,b]內的整數xi的個數。

lower_bound()返回乙個 iterator 它指向在[first,last)標記的有序序列中可以插入value,而不會破壞容器順序的第乙個位置,而這個位置標記了乙個大於等於value 的值。

也就是和上邊二分發的作用一樣,但是具體用法感覺像是stl相關的,苦逼我們居然沒怎麼學stl。暫且放一下。

章半小結:寫出來比單純看要好的多。。速度還行,晚上或者明天繼續寫剩下的。

演算法入門經典第八章學習筆記(中)

有乙個2 k 2 k個方格棋盤,恰有乙個方格是灰色的,其他為白色,你的任務是用包含3個方格的l型骨牌覆蓋所有白色方格。灰色方格不能被子覆蓋,且任意乙個白色方格不能同時被兩個或更多骨牌覆蓋。如圖8 3所示為l型骨牌 三格板 的4種旋轉方式。方法也是分治法,總牌數 4 k 1 3 劃分成下面 a 這樣,...

演算法競賽入門經典(第八章)

習題8 1 uva1149 11.8 include include using namespace std int main sort w 1,w n 1 l 1 r n num 0 while l r if mi w l 0 l num cout 習題8 2 uva1610 11.8 inclu...

第八章(筆記)

能在 中進行記憶體單元的定址的暫存器只有4個,分別是bx si di bp 其中bx bp 是基址,bx對應的段位址是ds,bp對應的段位址是ss si di 是變址,單獨使用時段位址是ds,組合使用段位址是跟隨組合的基址對應的段位址 中進行記憶體單元定址彙總 si di bx bp 常量 si 常...