【教學內容相關章節】
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 常...