日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划和摩尔投票法

發(fā)布時間:2025/3/21 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划和摩尔投票法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

動態(tài)規(guī)劃

維基百科對動態(tài)規(guī)劃(Dynamic programming,簡稱DP)的定義是一種在數(shù)學、管理科學、計算機科學、經(jīng)濟學和生物信息學中使用的,通過把原問題分解為相對簡單的子問題的方式求解復雜問題的方法

斐波那契數(shù)列

斐波那契數(shù)列是一個典型的可以把原問題分解為相對簡單的子問題的方式求解復雜問題的例子。下面是斐波那契數(shù)列的數(shù)學定義:

  • F(0)=0,F(1)=1
  • F(2)=F(1)+F(0)=1+0=1
  • F(n)=F(n-1)+F(n-2) (n>=2) 根據(jù)這個數(shù)學定義,我們可以用遞歸的方式很輕松的實現(xiàn)這個算法。
package mainimport ("fmt""time" )func fib(n int) int {if n <= 1 {return n}return fib(n-1) + fib(n-2) }func main() {start := time.Now().Unix()fib(45)end := time.Now().Unix()fmt.Println(end-start) } 復制代碼

上面代碼我們求的是F(45),代碼非常的簡單,但發(fā)現(xiàn)計算時間達到了6秒左右,效率十分低下,下面是根據(jù)剛剛的代碼畫出的一個F(5)的樹狀圖:

從圖中可以看出F(3)計算了2次,F(2)計算了3次,F(1)計算了4次,發(fā)生了很多重復計算,這也是造成效率低下的原因,要優(yōu)化的思路就是去除掉這些不必要的重復計算。現(xiàn)在我們將每個子問題的計算結(jié)果存儲起來,當再次碰到同一個子問題時,就可以直接從之前存儲的結(jié)果中取值,就不用再次計算了。比如第一次碰到計算F(2)時,可以用一個字典把F(2)的計算結(jié)果存儲起來,當再次碰到計算F(2)時就可以直接從字典中取值,改造后的代碼如下:

package mainimport ("fmt""time" )var m = map[int]int{0:0, 1:1} func fib(n int) int {if v, ok :=m[n]; ok {return v}m[n-1],m[n-2] =fib(n-1),fib(n-2)return m[n-1]+m[n-2] }func main() {start := time.Now().UnixNano()fib(45)end := time.Now().UnixNano()fmt.Println(end-start) } 復制代碼

經(jīng)過改造后再計算F(45)不到1秒。一旦某個給定子問題的解已經(jīng)算出,則將其記憶化存儲,以便下次需要同一個子問題解之時直接查表這也是動態(tài)規(guī)劃的重要內(nèi)容。

所以動態(tài)規(guī)劃兩個最主要的點是:

  • 將一個復雜的問題分解為若干多個子問題。
  • 將每個子問題的結(jié)果存儲起來,使每個子問題只解決一次。

House Robber

下面是用動態(tài)規(guī)劃的方法來解決 LeetCode 上一道名為 House Robber 的題目:

你是一個專業(yè)的小偷,計劃偷竊沿街的房屋。每間房內(nèi)都藏有一定的現(xiàn)金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統(tǒng),如果兩間相鄰的房屋在同一晚上被小偷闖入,系統(tǒng)會自動報警。 給定一個代表每個房屋存放金額的非負整數(shù)數(shù)組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。 假設現(xiàn)在各個房屋存放的金額分別為2、7、9、3、1,求最大能偷竊到的金額。

我們用 P(n) 表示為總共有 n 間房屋時能偷取到的最大金額,用 r(n) 表示為第 n 間房屋中存放的金額。 當 n 為1時 P(1)=r(1),n 為2時 P(2)=Max(r(1), r(2))。因為題目要求不能打劫相鄰兩間房,所以當有 n 間房時 P(n)=Max(P(n-2)+r(n), P(n-1))。用方程來表示就是:

P(1)=r(1) P(2)=Max(r(1), r(2)) P(n)=Max(P(n-2)+r(n), P(n-1)) 復制代碼

所以這個問題就被分解成了若干個子問題,下面是其代碼實現(xiàn):

package mainimport "fmt"var m = map[int]int{}func rob(arr []int) int {l := len(arr)if l <= 0 {return 0}if v,ok:=m[l-1];ok{return v}if l == 1 {m[0]=arr[0]return arr[0]}if l == 2 {if arr[0] >= arr[1] {m[1]=arr[0]return arr[0]} else {m[1]=arr[1]return arr[1]}}a, b:= rob(arr[:l-2])+arr[l-1],rob(arr[:l-1])if a>=b{m[l-1]=a} else {m[l-1]=b}return m[l-1] }func main() {arr := []int{2,7,9,3,1}m[0]=arr[0]ret :=rob(arr)fmt.Println(ret) } 復制代碼

上面的代碼就是我們根據(jù)方程無腦寫出的算法就已經(jīng)達到了偷竊最大金額的目的,但其實還是有一些優(yōu)化空間的,我們要計算 P(n) 其實只需要記住之前的 P(n-2) 和 P(n-1)就夠了,但我們其實將 P(1)、P(2)、...、P(n-2) 都記住了,帶來了一些內(nèi)存浪費,之所以會有這個問題是因為我們求解 P(n) 時會依次求解 P(n-1)、P(n-2)、...、P(1) 是一種自頂向下的求解方式,如果換成自底向上的求解方式可以寫出如下代碼:

package mainimport "fmt"func rob(arr []int) int {pre1, pre2 := 0, 0for _,v := range arr {if pre2+v >= pre1 {pre1,pre2 = pre2+v,pre1} else {pre1,pre2= pre1,pre1}}return pre1 }func main() {arr := []int{2,7,9,3,1}ret :=rob(arr)fmt.Println(ret) } 復制代碼

上面的變量 pre1 和 pre2 分別表示 P(n-1) 和 P(n-2),這樣通過自底向上的方式求出了結(jié)果,比自頂向下的方式更節(jié)省內(nèi)存。

所以動態(tài)規(guī)劃需要記住的幾個關(guān)鍵點是將復雜問題拆分成若干個子問題記住子問題的結(jié)果自頂向下自底向上

摩爾投票法

假如有10個人參與投票,有的人投給A,有的人投給B,有的人投給C,當我們想要找出A、B、C誰得票最多時,我們可以將兩個不同的投票作為一對進行刪除,直到不能再刪時然后再查看結(jié)果中還剩下的投票就是得票最多的那個。比如上述10個人的投票情況是[A,B,C,C,B,A,A,A,B,A],下面是進行刪除的過程:

[A,B,C,C,B,A,A,A,B,A]==>[C,C,B,A,A,A,B,A] //A,B為不同的投票所以可以//作為一對進行刪除 [C,C,B,A,A,A,B,A]==>[C,A,A,A,B,A] //C,C為相同的投票所以不刪除,然后// 再依次向后查找發(fā)現(xiàn)C,B不同可以刪除 [C,A,A,A,B,A]==>[A,A,B,A] [A,A,B,A]==>[A,A] 復制代碼

通過不斷的對不同的投票作為一對進行刪除,投票結(jié)果中最后只剩下了[A,A],所以A就是得票最多的。摩爾投票法的核心就是將序列中兩個不同的元素進行抵消或刪除,序列最后剩下一個元素或多個相同的元素,那么這個元素就是出現(xiàn)次數(shù)最多的元素

Majority Element

求眾數(shù)就是摩爾投票法的一個典型運用場景,比如有下面這道算法題:

給定一個大小為 n 的數(shù)組,找到其中的眾數(shù)。眾數(shù)是指在數(shù)組中出現(xiàn)次數(shù)大于 n/2 的元素。給定數(shù)組[2,2,1,1,1,2,2,4,5,2,3,2,2] 找出其眾數(shù)。

實現(xiàn)代碼如下:

package mainimport "fmt"func main() {arr := []int{2,2,1,1,1,2,2,4,5,2,3,2,2}maj, count := arr[0], 1for i:=1;i<len(arr);i++ {if maj == arr[i] {count++} else {if count == 0 {maj,count = arr[i],1continue}count--}}fmt.Println(maj) } 復制代碼

代碼中先假定數(shù)組的第一個元素就是眾數(shù),并用一個變量 count 來記錄這個眾數(shù)出現(xiàn)的次數(shù),當被迭代到的數(shù)與這個眾數(shù)相同時 count 就加1,不同時就做抵消操作,即 count 減1,當 count 為0時,就將被迭代到的數(shù)設為新的眾數(shù)并將 count 置1。

以上就是摩爾投票法的原理和應用。

轉(zhuǎn)載于:https://juejin.im/post/5caffda35188251b0b7a6f1a

總結(jié)

以上是生活随笔為你收集整理的动态规划和摩尔投票法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。