《趣題學演算法》 第1章1 3節加法原理和乘法原理

2021-09-23 16:23:25 字數 3564 閱讀 6922

1.3 加法原理和乘法原理

組合數學中有兩條著名的原理——加法原理和乘法原理。利用這兩條原理可以快速地解決一些計數問題。

加法原理:做一件事,完成它可以有n類辦法,在第一類辦法中有m1種不同的方法,在第二類辦法中有m2種不同的方法,……,在第n類辦法中有mn種不同的方法,那麼完成這件事共有n=m1+m2+m3+…+mn種不同方法。

乘法原理:做一件事,完成它需要分成n個步驟,做第一步有m1種不同的方法,做第二步有m2種不同的方法,……,做第n步有mn種不同的方法,那麼完成這件事共有n=m1×m2×m3×…×mn種不同的方法。

維基百科

問題描述

氣泡排序是一種簡單的排序演算法。該演算法反覆掃瞄欲排序的列表,比較相鄰元素對,若兩者順序不對,就將它們交換。這樣對列表的掃瞄反覆進行直至列表中不存在需要交換的元素為止,這意味著列表已經排好序。演算法之所以叫此名字,是緣於最小的元素就像「泡泡」一樣冒到列表的頂端,這是一種基於比較的排序演算法。

氣泡排序是一種非常簡單的排序演算法,其執行時間為o(n2)。每趟操作從列表首開始,以此比較相鄰項,需要時交換兩者。重複進行若干趟這樣的操作直至無需再做任何交換操作為止。假定恰好做了t趟操作,序列就按公升序排列,我們就說t為對此序列的氣泡排序趟數。下面是乙個例子。序列為「5 1 4 2 8」,對其施行的氣泡排序如下所示。

第一趟操作:

( 51 4 2 8 ) −> ( 1 5 4 2 8 ),比較頭兩個元素,並將其交換。

( 1 5 4 2 8 ) −> ( 1 4 5 2 8 ),交換,因為5 > 4。

( 1 4 5 2 8 ) −> ( 1 4 2 5 8 ),交換,因為5 > 2。

( 1 4 2 5 8 ) −> ( 1 4 2 5 8 )由於這兩個元素已經保持順序(8>5),演算法不對它們進行交換。

第二趟操作:

( 1 4 2 5 8 ) −> ( 1 4 2 5 8 )

( 1 4 2 5 8 ) −> ( 1 2 4 5 8 ),交換,因為4 > 2。

( 1 2 4 5 8 ) −> ( 1 2 4 5 8 )

( 1 2 4 5 8 ) −> ( 1 2 4 5 8 )

在t = 2趟後,序列已經排好序,所以我們說對此序列氣泡排序的趟數為2。

zx在演算法課中學習氣泡排序,他的老師給他留了乙個作業。老師給了zx乙個具有n個兩兩不等的元素的陣列a,並且已經排成公升序。老師告訴zx,該陣列是經過了k趟的氣泡排序得來的。問題是:a有多少種初始狀態,使得對其進行氣泡排序,趟數恰為k?結果可能是乙個很大的數值,你只需輸出該數相對於模20100713的剩餘。

輸入輸入包含若干個測試案例。

第一行含有乙個表示案例數的整數t (tleqslant100 000)。

跟著的是t行表示各案例的資料。每行包含兩個整數n和k(1leqslantnleqslant1,000,000, 0leqslantkleqslantn−1),其中n表示序列長度而k表示對序列進行氣泡排序的趟數。

輸出對每個案例,輸出序列的初始情形數對模20100713的剩餘,每個一行。

輸入樣例

3

3 03 1

3 2

輸出樣例

1

32

解題思路

(1)資料的輸入與輸出

根據輸入檔案格式的描述,首先在其中讀出測試案例個數t。然後依次讀取案例資料n和k。對每個案例計算進行k趟處理就能實現氣泡排序的陣列a[1..n]有多少種可能的初始狀態,並將所得結果作為一行寫入輸出檔案。

1 開啟輸入檔案inputdata

2 建立輸出檔案outputdata

3 從inputdata中讀取案例數t

4 for t←1 to t

5 do 從inputdata中讀取案例資料n, k

6 bublle-sort-rounds(n, k, k, x, count)

7 將count作為一行寫入outputdata中

8 關閉inputdata

9 關閉outpudata

其中,第6行呼叫過程bublle-sort-rounds(n, k, k, x, count)計算進行k趟處理就能實現氣泡排序的陣列a[1..n]有多少種可能的初始狀態,是解決乙個案例的關鍵。

(2)處理乙個案例的演算法過程

為方便計,我們假定序列a中的n個數為0,1,…,n-1。注意,氣泡排序的第k趟操作,總是將當前範圍(a[0..k−1])內的最大的元素推至當前範圍的最後位置a[k−1]。

除了針對趟數k=0的唯一初始狀態a[0..n−1]已經有序外,我們歸納k=k(1leqslantk

k=1時,初始狀態只能是1,…, n−1中的乙個元素不出現在自己應有的位置上,而其他元素均處於相對順序的位置上。對於1而言,它要出現在a[0]處;對於2而言,它可以出現在a[0]、a[1]處之一;…一般地,對於i(0

k=2時,初始狀態可以是1,…, n−1中的兩個元素不出現在應有的位置上,而其他元素均處於相對順序的位置上。對於0

一般地,k=k(1leqslantk

若將1~n-1中的k個數0

**bublle-sort-rounds(n, k, k, x, count) ▷k表示遞迴層次

1 if kgeqslantk ▷得到乙個積

2 then item←1

3 for i←1 to k

4 do item←(itemxi) mod 20100713

5 count←(count+item) mod 20100713

6 return

7 if k=0

8 then begin←n-1, end← k ▷頂層,x[0]的取值範圍

9 else begin←xk-1-1, end← k-k ▷k>1時,x[k]的取值範圍

10 for p←begin downto end ▷確定第k個因子

11 do xk ← p

12 bublle-sort-rounds(n, k, k+1)**

演算法1-10 計算具有n個不同元素恰做k趟操作完成排序的序列初始狀態數的過程

對測試案例資料n和k,上述過程執行如下。這是乙個遞迴過程。遞迴層次由引數k表示,表示計算乙個積中第k個因子。最頂層的呼叫應該是bublle-sort-rounds(n, k, 0, x, count),即k=0。

第1~7行當檢測到k>k時,意味著得到乙個積的所有因子。由於20100713是乙個素數,以它為模的剩餘類[3]對加法和乘法運算是封閉的,所以,可以對每一步乘法運算求關於模20100713的剩餘,也可以在將積累加到count時進行求關於模20100713的剩餘。

第7~8行if-then-else結構針對k是否為1決定第k個因子的取值範圍begin~end。

第9~12行的for迴圈完成對第k個因子xk的確定後,呼叫自身確定xk+1。

由於sum_}

解決本問題的演算法的c++實現**儲存於資料夾laboratory/bubble sort中,讀者可開啟檔案bubblesort.cpp研讀,並試執行之。

《趣題學演算法》 第1章1 5節置換與輪換

1.5 置換與輪換 設有n個兩兩不等的元素a1,a2,an構成的集合a,考慮a到自身的乙個1 1變換 a 1 a1 a 2 a2 a n an 換句話說,a 1,a 2,a n是a1,a2,an的乙個重排。數學中,稱這樣的對應關係 為a的乙個置換。例1 集合a 2 1,4 2,3 3,1 4就是a上...

《趣題學演算法》 第0章0 4節演算法的正確性

0.4 演算法的正確性 解決乙個計算問題的演算法是正確的,指的是對問題中任意合法的輸入均應得到對應的正確輸出。大多數情況下,問題的合法輸入無法窮盡,當然就無法窮盡輸出是否正確的驗證。即使合法輸入是有限的,窮盡所有輸出正確的驗證,在實踐中也許是得不償失的。但是,無論如何,我們需要保證設計出來的演算法的...

《SOA達人迷》 第1章1 3節SOA案例分析

1.3 soa案例分析 soa達人迷 大約在150年以前,有一家名叫abc的保險公司,通過向工廠和製造商銷售保險單起家。早期沒有電腦,公司業務還算井井有條,沒有被搞得一團糟。公司的業務過程非常簡單,客戶發來信件申請保險,公司的業務人員確定利率,銷售保險,並希望不要發生著火或意外事件。abc公司繁榮了...