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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

树规总结

發布時間:2025/6/15 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 树规总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
之所以這樣命名樹規,是因為樹規的這一特殊性:沒有環,dfs是不會重復,而且具有明顯而又嚴格的層數關系。利用這一特性,我們可以很清晰地根據題目寫出一個在樹(型結構)上的記憶化搜索的程序。而深搜的特點,就是“不撞南墻不回頭”。這一點在之后的文章中會詳細的介紹。


? ? ? ?首先是掃盲,介紹幾條名詞的專業解釋以顯示我的高端(大部分人可以略過,因為學習到樹規的人一下應該都懂……):


動態規劃:?


  問題可以分解成若干相互聯系的階段,在每一個階段都要做出決策,全部過程的決策是一個決策序列。要使整個活動的總體效果達到最優的問題,稱為多階段決策問題。動態規劃就是解決多階段決策最優化問題的一種思想方法。


階段:


  將所給問題的過程,按時間或空間(樹歸中是空間,即層數)特征分解成若干相互聯系的階段,以便按次序去求每階段的解。


狀態:


  各階段開始時的客觀條件叫做狀態。


決策:


  當各段的狀態取定以后,就可以做出不同的決定,從而確定下一階段的狀態,這種決定稱為決策。 (即孩子節點和父親節點的關系)


?


策略:


  由開始到終點的全過程中,由每段決策組成的決策序列稱為全過程策略,簡稱策略。


狀態轉移方程:


  前一階段的終點就是后一階段的起點,前一階段的決策選擇導出了后一階段的狀態,這種關系描述了由k階段到k+1階段(在樹中是孩子節點和父親節點)狀態的演變規律,稱為狀態轉移方程。


?


目標函數與最優化概念:


  目標函數是衡量多階段決策過程優劣的準則。最優化概念是在一定條件下找到一個途徑,經過按題目具體性質所確定的運算以后,使全過程的總效益達到最優。


樹的特點與性質:


1、 有n個點,n-1條邊的無向圖,任意兩頂點間可達


2、 無向圖中任意兩個點間有且只有一條路


3、 一個點至多有一個前趨,但可以有多個后繼


4、 無向圖中沒有環;


?


廢話說完了,下面是正文:


? ? ? ?拿到一道樹規題,我們有以下3個步驟需要執行:


判斷是否是一道樹規題:即判斷數據結構是否是一棵樹,然后是否符合動態規劃的要求。如果是,那么執行以下步驟,如果不是,那么換臺。
建樹:通過數據量和題目要求,選擇合適的樹的存儲方式。如果節點數小于5000,那么我們可以用鄰接矩陣存儲,如果更大可以用鄰接表來存儲(注意邊要開到2*n,因為是雙向的。這是血與淚的教訓)。如果是二叉樹或者是需要多叉轉二叉,那么我們可以用兩個一維數組brother[],child[]來存儲(這一點下面會仔細數的)。
寫出樹規方程:通過觀察孩子和父親之間的關系建立方程。我們通常認為,樹規的寫法有兩種:
a.根到葉子: 不過這種動態規劃在實際的問題中運用的不多。本文只有最后一題提到。


b.葉子到根: 既根的子節點傳遞有用的信息給根,完后根得出最優解的過程。這類的習題比較的多。


注意:這兩種寫法一般情況下是不能相互轉化的。但是有時可以同時使用具體往后看。


?


以下即將分析的題目的目錄及題目特點:


1、加分二叉樹:區間動規+樹的遍歷;


2、二叉蘋果樹:二叉樹上的動規;


3、最大利潤:多叉樹上的動規;


4、選課:多叉樹轉二叉;


5、選課(輸出方案):多叉轉二叉+記錄路徑;


6、軟件安裝:判斷環+縮點+多叉轉二叉;


【4、5、6屬于依賴問題的變形】


?


  基本的知識掌握和步驟了,我們就通過習題來感受一下樹規的魅力,先來看這樣一道題:


1、加分二叉樹


【問題描述】


? ? 設一個n個節點的二叉樹tree的中序遍歷為(l,2,3,…,n),其中數字1,2,3,…,n為節點編號。每個節點都有一個分數(均為正整數),記第i個節點的分數為di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:


? ? subtree的左子樹的加分× subtree的右子樹的加分+subtree的根的分數


? ? 若某個子樹為空,規定其加分為1,葉子的加分就是葉節點本身的分數。不考慮它的空子樹。


? ? 試求一棵符合中序遍歷為(1,2,3,…,n)且加分最高的二叉樹tree。要求輸出;


? ? (1)tree的最高加分


? ? (2)tree的前序遍歷


【輸入格式】


? ? 第1行:一個整數n(n<30),為節點個數。


? ? 第2行:n個用空格隔開的整數,為每個節點的分數(分數<100)。


【輸出格式】


? ? 第1行:一個整數,為最高加分(結果不會超過4,000,000,000)。


第2行:n個用空格隔開的整數,為該樹的前序遍歷。


?


【算法&思路】:


  看到這個問題,我們首先應該想到的是這道題是否屬于動態規劃,而這里我們發現,結合問題,如果整棵樹的權值最大,必然有左子樹的權值最大,右子樹的權值也最大,符合最優性原理。所以是動態規劃。而卻不是一道樹規的題目。因為我們可以用區間動規的模型解決掉:直接定義一個f[i][j]表示從i到j的最大值,則f[i][j]=max(f[i][k-1]*f[k+1][j]+a[k]),枚舉k即可。接下來是如何建樹的問題,只有把樹建好了,才能輸出其前序遍歷。于是,我們看到了兩個關鍵詞:二叉樹,中序遍歷。有了這兩個關鍵詞,加上區間動規,這棵樹就能建起來了。根據二叉樹的特性來建樹(這里不再具體討論樹的詳細的構造了,中序遍歷和前序遍歷不懂得自己百度)。所以這顆樹的前序遍歷,只需要邊動規邊記錄下root[i][j]=k表示i到j的根為k即可確定樹的構造。


【代碼】:


View Code
?


【小結】:拿到一道題目,首先我們要做的是看清題目,判斷這是一道考察什么算法的題目。只有建立在正確思路基礎下的算法,才是有意義的,正確的算法,也是事半功倍的算法。而此題是批著 樹形 外觀的 非樹形動態規劃題。而真正的樹形動態規劃是在樹上做動態規劃。


?


  真正的樹規來了。


?


2、二叉蘋果樹


【題目描述】:


有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有只有1個兒子的結點)這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。我們用一根樹枝兩端連接的結點的編號來描述一根樹枝的位置。現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。
給定需要保留的樹枝數量,求出最多能留住多少蘋果。


【輸入格式】:


第1行2個數,N和Q(1<=Q<= N,1<N<=100)。
N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的信息。
每行3個整數,前兩個是它連接的結點的編號。第3個數是這根樹枝上蘋果的數量。
每根樹枝上的蘋果不超過30000個。


【輸出格式】:


剩余蘋果的最大數量。


input


5 2


1 3 1


1 4 10


2 3 20


3 5 20


output


21


?


【算法&思路】:首先,可以肯定的是,這是一道有關樹規的題目,父節點和子節點存在著相互關聯的階段關系。


  第一步完成。再執行第二步:我們觀察到題目數據量不大,所以有兩種選擇:鄰接矩陣和鄰接表。因為鄰接矩陣的代碼簡單,思路清晰,所以建議能寫鄰接矩陣的時候就不要寫鄰接表了。我們設ma[x][y]為邊的值,因為樹是雙向的,所以要再記錄ma[y][x]。


  設tree[v,1]為節點v的左子樹,tree[v,2]為節點v的右子樹,然后我們再遞歸建樹(因為樹是遞歸定義的,所以很多時候建樹都要考慮遞歸)。


  建樹的問題解決的了,我們就要列狀態轉移方程了。根據求什么設什么的原則,我們定義f[i][j]表示以i為節點的根保留k條邊的最大值,那么f[v][k]=max(f[v][k],(f[tree[v][1]][i]+f[tree[v][2]][k-i-1]+num[v])),我們枚舉i就可以了。正如我開頭提到的。因為樹是遞歸定義的所以我們可以用記憶化搜索的形式(dfs)來具體實現。而樹本身嚴格分層,而且沒有環。所以是不會重復的。


F[1][Q+1]就是答案。因為題目中給的是邊的權值,而我們在處理時將每條邊的權值全賦給其所連的父節點和子節點中的子節點(將關于邊的問題轉化為關于點的問題),所以最后是Q+1,表示點的數目。


?【代碼】:


View Code
?


【小結】:在樹的存儲結構上,我們一般選的都是二叉樹,因為二叉樹可以用靜態數組來存儲,并且狀態轉移也很好寫(根節點只和左子節點和右子節點有關系)。


  可如果是多叉怎么辦? ? ? ? 往下看。


?


3、最大利潤


【題目描述】


政府邀請了你在火車站開飯店,但不允許同時在兩個相連接的火車站開。任意兩個火車站有且只有一條路徑,每個火車站最多有50個和它相連接的火車站。
告訴你每個火車站的利潤,問你可以獲得的最大利潤為多少。
最佳投資方案是在1,2,5,6這4個火車站開飯店可以獲得利潤為90


【輸入格式】


第一行輸入整數N(<=100000),表示有N個火車站,分別用1,2。。。,N來編號。接下來N行,每行一個整數表示每個站點的利潤,接下來N-1行描述火車站網絡,每行兩個整數,表示相連接的兩個站點。


【輸出格式】


輸出一個整數表示可以獲得的最大利潤。


【樣例輸入】


6 10


?20


25


40


30


30


4 5


1 3


3 4


2 3


6 4


【樣例輸出】


90


?


【算法&思路】:


  按照上一題的步驟,我們再來分析一遍:一、是否是動態規劃。這時可能很多人已經吐槽了:閉著眼都知道是動態規劃,不然你粘出來干什么??呵呵,沒錯,確實是。但是為什么是呢??首先,這是棵樹,是一棵多叉樹。其次,當我們嘗試著把他向動態規劃上靠時,我們發現當前節點只與其孩子節點的孩子節點(這里沒打錯,因為隔一個火車站)有關系。所以綜上所述,是動規,還是一個樹規,一個不折不扣的樹規!


  接下來,第二步建樹。看范圍和題目發現,這是一個有著n(<100000)的多叉樹,所以只能用鄰接表存儲了。沒有根,我們一般通常指定1為根。


  第三步:F[i]表示i這條根要,G[i]表示不要(也可以用f[i][1,0]來表示)。然后以此枚舉i的孩子:如果i要了那么i的孩子就不能要,如果i不要i的孩子就可要可不要(取最大值)即可。最后輸出max(f[1],g[1]);


?【代碼】:


View Code
?


【小結】:無論是多叉樹還是二叉樹,只要我們把樹以正確的形式建立起來,那么我們再根據建樹的形式和題目要求,找出孩子和父親之間的關系,那么狀態轉移方程很容易就求解出來了。多叉其實也不是很難。對么?呵呵,那么再看下面一道題:


?


4、選課


【題目描述】


  學校實行學分制。每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。學校開設了N(N<300)門的選修課程,每個學生可選課程的數量M是給定的。學生選修了這M門課并考核通過就能獲得相應的學分。
  在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其它的一些課程的基礎上才能選修。例如《Frontpage》必須在選修了《Windows操作基礎》之后才能選修。我們稱《Windows操作基礎》是《Frontpage》的先修課。每門課的直接先修課最多只有一門。兩門課也可能存在相同的先修課。每門課都有一個課號,依次為1,2,3,…。


 你的任務是為自己確定一個選課方案,使得你能得到的學分最多,并且必須滿足先修課優先的原則。假定課程之間不存在時間上的沖突。


?


【輸入格式 】Input Format


?


?


輸入文件的第一行包括兩個整數N、M(中間用一個空格隔開),其中1≤N≤300,1≤M≤N。
以下N行每行代表一門課。課號依次為1,2,…,N。每行有兩個數(用一個空格隔開),第一個數為這門課先修課的課號(若不存在先修課則該項為0),第二個數為這門課的學分。學分是不超過10的正整數。


?


?


?


?


?


?


?


【輸出格式】 Output Format


?


?


只有一個數:實際所選課程的學分總數。


?


【算法&思路】:


  繼續照著三步的方法判斷:一,題目大致一看,有點像有依賴的背包問題,于是你扭頭就走,關掉了我的《樹規》,打開了崔神犇的《背包九講》。然后你哭了,因為有依賴的背包問題只限定于一個物品只依賴于一個物品,而沒有間接的依賴關系。有依賴的背包問題的模型,根本解決不了。崔神告訴你,這屬于樹規的問題,不屬于他背包的范圍了。好了,回過來,我們接著分析。發現這是一棵樹,還是一棵多叉樹,嗯,很好,確定是樹規了。


  然后第二步,建樹,一看數據范圍鄰接矩陣;


  第三步動規方程:f[i][j]表示以i為節點的根的選j門課的最大值,然后有兩種情況: i不修,則i的孩子一定不修,所以為0;i修,則i的孩子們可修可不修(在這里其實可以將其轉化為將j-1個對i的孩子們進行資源分配的問題,也屬于背包問題);答案是f[1][m]。問題圓滿解決,一氣呵成。


  但……


  身為追求完美的苦*程序猿的我們,不可以將它更簡單一點呢?


  多叉轉二叉。


  因為之前我們說過“在樹的存儲結構上,我們一般選的都是二叉樹,因為二叉樹可以用靜態數組來存儲,并且狀態轉移也很好寫(根節點只和左子節點和右子節點有關系)。”所以轉換成二叉樹無疑是一種不錯的選擇。


  我們開兩個一維數組,b[i](brother)&c[i](child)分別表示節點i的孩子和兄弟,以左孩子和右兄弟的二叉樹的形式存儲這樣,根節點之和兩個節點有關系了,狀態轉移的關系少了,代碼自然也就好寫了。


  我們依舊f[i][j]表示以i為節點的根的選j門課的最大值,那么兩種情況:1.根節點不選修則f[i][j]=f[b[i]][j];2.根節點選修f[i][j]=f[c[i]][k]+f[b[i]][j-k-1]+a[i](k表示左孩子學了k種課程);取二者的最大值即可。


【代碼】:


View Code
?


【小結】:當題目中的數據結構是多叉樹的時候,我們有兩種選擇:直接在多叉樹上動規,或者轉化為二叉樹后動規。毫無疑問,二叉樹上的動規是簡潔的。但是,并不是說所有的多叉樹都需要轉化,一般情況下,當根節點與孩子節點有著必然的關系時才會轉化。這需要我們多做題目,增加對樹規的感覺才能游刃有余。


?


  我們繼續擴展:如果上一道題目繼續提問,要求輸出所選的方案呢?


?


5、選課(輸出方案)


【題目描述】同上。


【輸入格式】同上。


【輸出格式】 Output Format


?


?


第一行只有一個數,即實際所選課程的學分總數。
以下N行每行有一個數,表示學生所選課程的課號。
n行學生選課的課號按從小到大的順序輸出。


? ?
?


【算法&思路】:拿到這道題目,首先我們必然要和上一道題目做一下對比。對比后我們發現,這道題目和上第一道題目完全一樣,除了問題比上一題多一問:輸出方案。所以,我們可以把這道題目分成兩部分:求總數和輸出方案。而求總數的問題我們在上一題中已經很好的解決了,所以這道題目重點是考察的是樹的路徑記錄的問題。


  既然數是遞歸定義的,所以我們依舊使用遞歸的形式來記錄路徑:使用一個bool數組ans來進行遞歸,分兩種情況:取(1)和不取(0)。然后,我們繼續利用已經求得的f[i][j]的值來思考如何找到路徑:首先定義一個path()函數。如果f[i][j]=f[b[i]][j],那么節點i必然沒有取,讓ans[i]=0;否則,節點i一定取到了。(為什么呢?其實,這是依照第一問的dfs來思考的,第一問的dfs是這樣定義的,所以我們就這樣考慮了。)然后依照上一問,if(f[x][y]==f[b[x]][k-1]+f[c[x]][y-k]+s[x]),那么我們在i節點后選的一定是以上的方案,在這時讓ans[i]=1,繼續深搜path()即可。最后從1到n依次輸出取到的點即可。


?


View Code
?


【小結】:路徑輸出的問題,在不同的題目中有不同的解法,比如說邊求值邊記錄等等,而在樹規中,利用動規和樹的特點,求解完后再原路返回找一遍,是一種比較容易想到且容易操作的性價比比較高的算法。


?


6、軟件安裝


【題目描述】:


現在我們的手頭有N個軟件,對于一個軟件i,它要占用Wi的磁盤空間,它的價值為Vi。我們希望從中選擇一些軟件安裝到一臺磁盤容量為M的計算機上,使得這些軟件的價值盡可能大(即Vi的和最大)。


但是現在有個問題:軟件之間存在依賴關系,即軟件i只有在安裝了軟件j(包括軟件j的直接或間接依賴)的情況下才能正確工作(軟件i依賴軟件j)。幸運的是,一個軟件最多依賴另外一個軟件。如果一個軟件不能正常工作,那么他能夠發揮的作用為0。
我們現在知道了軟件之間的依賴關系:軟件i依賴Di。現在請你設計出一種方案,安裝價值盡量大的軟件。一個軟件只能被安裝一次,如果一個軟件沒有依賴則Di=0,這是只要這個軟件安裝了,它就能正常工作。


?


【輸入格式】:
第1行:N,M (0<=N<=100,0<=M<=500)
第2行:W1,W2, … Wi, … ,Wn
第3行:V1,V2, … Vi, … ,Vn
第4行:D1,D2, … Di, … ,Dn


【輸出格式】:
一個整數,代表最大價值。


【樣例】


3 10


5 5 6


2 3 4


0 1 1


?


5


?


【算法&思路】:同樣,這道題目類似與第4題,是一個依賴的問題,毫無疑問是一道動態規劃,但是它確實是樹規么?我們來想這樣一組數據,1依賴2,2依賴3,3依賴1。這樣符合題目要求,但有形成了環,所以不是一棵樹了。但是根據題目,這樣特殊的情況,要么全要,要么全就不要。所以,事實上我們可以將這個環看成一個點再來動規,即縮點。如何判斷是否是一個環呢,依照數據范圍,我們想到了floyed(弗洛里德),這是在這種數據范圍內性價比最高的方式。最后樹規。于是一個比較清晰的步驟就出來了:判環,縮點,樹規。


  接下來是細節:首先存樹,毫無疑問,是鄰接矩陣。


  做floyed。如果兩點之間mapp[i][j]中有另一條路徑相連,即mapp[i][k]=1 && mapp[k][j]=1(1表示兩點是通的);那么mapp[i][j]也是通的且是環。


  縮點。這個是最麻煩的,麻煩在于我們要把縮的點當成一個新點來判斷,而且要判斷某個點是否在某個環里。我們用染色法來判斷,用所占的空間w控制顏色的對應,有以下三種情況:1、點i所在的環之前沒有判斷過,是新環。那么,我們將這個新環放到數組最后,即新加一個點,然后讓這兩個點的空間標記為負值tmpw,且tmpw+tmpn(新點的下標)等于原來的點數,這樣,我們就可以通過某個點的空間迅速找到他所在的新點。像鑰匙一樣一一對應;2、點i所在的環之前已經判斷過了,是舊環(已合成新點),且i是環的一部分。那么我們就把i也加到這個新點里面,即體積,價值相加即可;3、點j所在的環是舊環,但是i不是環的一部分(例如1依賴2,2依賴3,3依賴1。4也依賴1,那么,4所在的是個環,但4不屬于環的一部分)。那么,把j的父親轉到新點上d[j]= n-w[d[j]]。


  以上縮點的工作做完之后,剩下的就是一棵樹。就可以在這上面動規了:先將其轉換成一棵左孩子右兄弟的二叉樹,之后記憶化。i的孩子不取f[b[x]][k]=dfs(b[x],k);還是取:f[c[x]][y-i]=dfs(c[x],y-i); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?                           ?f[b[x]][i]=dfs(b[x],i); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?                           ?f[x][k]=max(f[x][k],v[x]+f[c[x]][y-i]+f[b[x]][i]);


  最后答案是f[c[0]][m]。


【代碼】:




復制代碼
? 1 #include<iostream>
? 2 #include<iomanip>
? 3 #include<cstring>
? 4 #include<cmath>
? 5 #include<cstdio>
? 6 #include<cstdlib>
? 7 #include<string>
? 8 #include<memory>
? 9 #include<climits>
?10 #include<vector>
?11 #include<map>
?12 #include<queue>
?13 #include<algorithm>
?14 using namespace std;
?15?
?16 const int e=505;
?17 int n,m,tmpw=0,tmpn;
?18 int w[e]={0},v[e]={0},b[e]={0},c[e]={0},f[e][5*e]={0},d[e]={0};
?19 bool mapp[e][e]={0};
?20?
?21 void floride()
?22 {
?23 ? ? for(int i=1;i<=n;i++)//弗洛里德判斷是否有環;
?24 ? ? ? ? for(int j=1;j<=n;j++)
?25 ? ? ? ? ? ? for(int k=1;k<=n;k++)
?26 ? ? ? ? ? ? ? ? if(mapp[k][i]==1 && mapp[i][j]==1)
?27 ? ? ? ? ? ? ? ? ? ? mapp[k][j]=1;
?28 }
?29?
?30?
?31 void merge()//合點
?32 {
?33 ? ? tmpn=n;
?34 ? ? for(int i=1;i<=tmpn;i++)
?35 ? ? ? ? for(int j=1;j<=tmpn;j++)
?36 ? ? ? ? {
?37 ? ? ? ? ? ? if(mapp[i][j]==1 && mapp[j][i]==1 && i!=j && w[i]>0 && w[j]>0)//如果是新環;
?38 ? ? ? ? ? ? {
?39 ? ? ? ? ? ? ? ? tmpn++;
?40 ? ? ? ? ? ? ? ? v[tmpn]=v[i]+v[j];
?41 ? ? ? ? ? ? ? ? w[tmpn]=w[i]+w[j];
?42 ? ? ? ? ? ? ? ? tmpw--; ? ?w[i]=tmpw; ? ?w[j]=tmpw; ? ?//tmpw+tmpn永遠等于最開始的n
?43 ? ? ? ? ? ? }
?44 ? ? ? ? ? ??
?45 ? ? ? ? ? ? //如果j依賴的點被合并(是舊環),且j在環里
?46 ? ? ? ? ? ? if(w[d[j]]<0 && w[j]>0 && mapp[j][d[j]]==1 && mapp[j][d[j]]==1) ? ?
?47 ? ? ? ? ? ? {
?48 ? ? ? ? ? ? ? ? w[n-w[d[j]]]+=w[j];
?49 ? ? ? ? ? ? ? ? v[n-w[d[j]]]+=v[j];
?50 ? ? ? ? ? ? ? ? w[j]=w[d[j]];
?51 ? ? ? ? ? ? }
?52 ? ? ? ? ? ??
?53 ? ? ? ? ? ? //如果j依賴的點在環里,但是j不在環里
?54 ? ? ? ? ? ? if(w[d[j]]<0 && w[j]>0)
?55 ? ? ? ? ? ? ? ? if((mapp[j][d[j]]==1 && mapp[d[j]][j]==0) || (mapp[j][d[j]]==0 && mapp[d[j]][j]==1))
?56 ? ? ? ? ? ? ? ? ? ? d[j]=n-w[d[j]];
?57 ? ? ? ? }
?58 }
?59?
?60 int ?dfs(int x,int k)
?61 {
?62 ? ? if(f[x][k]>0) ? ?return(f[x][k]);
?63 ? ? if(x==0 || k<=0) ? ?return(0);
?64 ? ? //不取x
?65 ? ? f[b[x]][k]=dfs(b[x],k);
?66 ? ? f[x][k]=f[b[x]][k];
?67 ? ? ? ? int y=k-w[x];
?68 ? ? ? ? for(int i=0;i<=y;i++)
?69 ? ? ? ? {
?70 ? ? ? ? ? ? f[c[x]][y-i]=dfs(c[x],y-i);
?71 ? ? ? ? ? ? f[b[x]][i]=dfs(b[x],i);
?72 ? ? ? ? ? ? f[x][k]=max(f[x][k],v[x]+f[c[x]][y-i]+f[b[x]][i]);
?73 ? ? ? ? }
?74 ? ? return(f[x][k]);
?75 }
?76?
?77?
?78?
?79 int main()
?80 {
?81 ? ? //freopen("in.in","r",stdin);
?82 ? ? cin>>n>>m;
?83 ? ? for(int i=1;i<=n;i++)
?84 ? ? ? ? scanf("%d",&w[i]);
?85 ? ? for(int i=1;i<=n;i++)
?86 ? ? ? ? scanf("%d",&v[i]);
?87 ? ? for(int i=1;i<=n;i++)
?88 ? ? {
?89 ? ? ? ? int a;
?90 ? ? ? ? scanf("%d",&a);
?91 ? ? ? ? d[i]=a;
?92 ? ? ? ? mapp[a][i]=1;
?93 ? ? }
?94 ? ??
?95 ? ? floride();
?96 ? ? merge();
?97 ? ??
?98 ? ? //多叉轉二叉
?99 ? ? for(int i=1;i<=tmpn;i++)
100 ? ? ? ? if(w[i]>0)
101 ? ? ? ? {
102 ? ? ? ? ? ? b[i]=c[d[i]];
103 ? ? ? ? ? ? c[d[i]]=i;
104 ? ? ? ? }
105 ? ? cout<<dfs(c[0],m);
106?
107 ? ??
108 ? ? fclose(stdin); ? ?fclose(stdout);
109 ? ? return 0;
110 }
復制代碼
?


【小結】:依賴問題的變化很多,比如基本樹規,記錄路徑,有環等等。但是他們都有一些共同的特點,比如說記憶化的方程差不多。根據題目,我們應該會判斷給的數據是否是圖,是樹,能想出特殊情況。樹畢竟是圖的一種特殊形式,而二叉樹又是樹的一種特殊形式。如果能將一個問題由復雜向簡單轉換,那么我們不僅思路會清晰很多,代碼量也會少很多。下面一道題目,就是我們所說的很少見的根節點向葉子節點動規的問題。


?


【總結】:樹規是動態規劃的一種,它將樹和動態規劃很巧妙地結合在了一起。做樹規題目,不僅僅鍛煉了我們的代碼能力,而且加深了我們對動態規劃的理解。再次強調,樹的遞歸定義使樹規多以記憶化的形式來寫,而由于樹的嚴格分層,使動規的階段自然就清晰了起來,多找一找父節點與子節點的關系,就是很可能是兩個階段之間的聯系。

總結

以上是生活随笔為你收集整理的树规总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。