當我仍是一個被P哥哥忽悠來的無知少年時,覺得編程只有C語言那麼點東西,半個學期學完C語言的我覺得天下無敵了,誰知自從有了杭電練習題以後,才發現本身簡直就是渣渣……咳咳進入正題:html
成長爲一名絕世高手,入門是必須的,吶,入門第一步,ACM程序設計書一本,裏面記載了STL各類花式招數,實戰之中方便快捷,實在是各武林人士入門必選~自此我等一行數十人踏上了課程練習這一不歸路,這次訓練慘烈程度異常,由於正值寒假因此各位師兄弟被過年這一障礙干擾,沒法專心修煉,通過了半個寒假的闖關我終於通關,可是沒想到還有下一層關卡在等着咱們。算法
模擬,特別是大模擬,在實戰中是最爲頭疼的了,代碼長,小數據多,可是這時候剛剛接觸ACM的我並不知道這是模擬,起初我還覺得這是給咱們的水題,可是天真的我後來發現——我滴媽耶,這是個什麼東西,模擬下棋,模擬打牌,模擬發牌,模擬生物實驗,模擬……天哪出題人有這麼無聊麼,幾百行代碼,一不當心哪裏錯了又得一點點的調真是頭疼,模擬最大的陷阱就是,題意很清楚,叫你很是想寫,可是就是很麻煩讓你又不很想寫,糾結啊。可是平時練習嘛。編程
今後立志開學前將這套題寫完的我,開始了沒日沒夜的擼代碼日子,可是一天一個題的速度實在是太慢了,直到開學也沒能把這套題寫完;數組
開學以後掌門人正式的開班收徒了,第一個專題就是貪心,另外輔助點的東西就是二分三分;優化
貪心算法就跟名字同樣解決問題必定要擔憂,拿田忌賽馬這個例題來說,他和大王賽馬的原則就是不能吃虧,能贏的就要贏,贏不了就要用本身最次的馬和你最好的馬跑,浪費掉你最好的馬。編碼
貪心的通常過程就是:url
(1)
(2)
二分可用於具備單調性的直線求零點,經過中間值與兩段的值的比較不斷地縮小範圍就能夠在必定的精度範圍內求出解。通常的解體模板爲:code
While(abs(left-right)>1e-6)
{
If(F(mid)>0)
Else
}
三分可用於曲線求極值,通常模板爲:
while(fabs(left-right)>1e-6)//三分來判斷最大的那個角度
{
}
一入搜索深似海,不知何年是歸期,^0^萬能的搜索沒有點空間想象能力學這個真費勁。
這是一個心酸的故事,你可能不信,一開始我是拒絕的,5555555.沒有什麼問題是搜索解決不了的,若是有那就寫兩個搜索。學搜索開始的日子老是難熬的,廣搜還好點,深搜就費勁了,遞歸沒學明白,這個地方更是稀裏糊塗的,看了一遍遍代碼仍是不明白,只能一遍遍的走程序,真是煎熬,深搜狀態多,就拿聯通塊的問題來講,每一步均可能搜出來四步,很難弄的,光寫就會寫不少,好容易的把這個弄明白了,可是又迎來了又一大難題——超時!!!!!得想盡辦法來剪枝,好煩啊!!!
先將樹頂的元素壓入棧中,將他全部的兒子都搜出來壓入棧中,這個搜索的元素就沒有價值了,就能夠將它出棧,接着只要棧不空就繼續剛纔的操做,直到棧空了,或者你找到你想要的解了。
深搜這裏就不放圖了(一個圖得弄很長時間好麻煩滴說),就是一條路徑找到頭,直到盡頭就返回搜索結果。不是我懶!!!我不懶!!!深搜我就理解了這麼多,哼~
記憶化搜索和DP有些相似,用數組記錄下來搜索的狀態,每次搜索獲得的結果和上一個狀態進行比較取最優的結果,惟一有點不一樣的就是,DP通常是從後往前更新狀態,可是記憶化搜索是從前日後更新狀態。
上學期一次機緣巧合之下得知了,動態規劃這一武林至高祕籍,但也只是皮毛。
那是一個只有openjudge的日子,基礎題作了幾十道,就開始找找第二頁有沒有能作的題,採藥!,這個題只有幾行,就是一個最標準的01揹包問題,以後……就沒有以後了,這是什麼鬼,問P哥哥這題怎麼作,他說動態規劃,還給我講了怎麼寫,DP[i][j]表示第i個物品時,存放j大的物品的最優價值……等等,這都是什麼鬼,這是什麼,徹底不明白他在說什麼。回去看講課視頻動態規劃,開始有那麼一點懂了,可是接下來就有杭電練習題,就把這個放過去了。
幸虧咱們有動態規劃這個專題,這一下可找到好東西了,好多招式,01揹包,多重揹包,徹底揹包……(等等,怎麼全是揹包),很差意思先了解了解,還沒看更多的東西。
把這個過程理解下:在前i件物品放進容量v的揹包時,
它有兩種狀況:
For(int i=1;i<=n;i++)
For(int j=v;j>=c[i];j--)
F[i][j]=max(F[i-1][v],F[i-1][j-c[i]]+w[i]);
第一種是第i件不放進去,這時所得價值爲:f[i-1][v]
第二種是第i件放進去,這時所得價值爲:f[i-1][v-c[i]]+w[i]
這個圖清晰明瞭,每次更新狀態都是i行和i-1行做比較,誰的狀態更優就取哪個,最優解就是最後一個狀態f[n][v];
徹底揹包:
僞代碼:
for i=1..N
for(int j=c[i];j<=v;j++)
max();
j=c[i]+1
代碼:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}
解析:
這裏的max中的兩項就是當前狀態的值了,爲什麼?
由於每種揹包都是無限的。當咱們把i從1到N循環時,f[v]表示容量爲v在前i種揹包時所得的價值,這裏咱們要添加的不是前一個揹包,而是當前揹包。因此咱們要考慮的固然是當前狀態。
多重揹包能夠這麼理解遍歷到當前物品時若是物品的整體積小於揹包的剩餘體積那麼就能夠當作是一個01揹包問題,若是大於的話就能夠當作是一個徹底揹包問題;這樣多重揹包問題就能解決了。
徹底揹包還有一個插曲:二進制優化,這樣能夠大大的節省時間,由於任何數都能由幾個二進制數和一個非二進制數組成,好比9=2*2*2+1;這樣就能將徹底揹包轉化爲01揹包,將二進制數當作物品,取仍是不取,找到最有的解決方案
給定一個數列:ans[]={1,7,3,5,9,4,8};
顯然此序列的最長上升自學列爲1,3,4,8,長度爲4
怎麼求?
用一個數組DP[i]表示以ans[i]爲結尾的子序列的最大長度,DP[i]中的每個元素對應每個狀態。
ans[]:1
從頭開始->
並查集就是合併查找,最短路徑;
(1)「建樹」:通俗的建樹,就是將全部聯通的點用一個標記標記出來,就造成了一個「數」,例如在1-10這十個點中,1 3聯通那麼1 3的就能夠用相對小的1來標記。
(2)查找:想查找兩個點是否是聯通的時候,只須要找到兩個點的根是否是同一個根,若是是那麼就是聯通,不是就不是聯通。
1,2,4是四個定點其餘的是距離,從2到4最直接的就是2-4,可是不是最近的,須要舒展一下2-1-4,這樣只有8.因此纔是最短的。這個過程就是狄克斯特拉算法。下面進入正題:
咱們這裏定義圖的編號爲:
1 2 3
4 5 6
7 8 9
圖1:初始化的圖,其中包含邊的權值(耗時)。(這裏圖是有向圖)。
圖2:肯定起點,而後向能直接走到的點走一下,記錄此時的估計值:2 6 9.。
圖3:找到距離起點最近的點,是正東邊的那個點,這時候咱們耗費權值爲2。而後咱們進行鬆弛操做,從起點到其東南方的點直接到的權值耗費爲6,可是咱們經過剛剛選定的點,咱們找到了到這個點更近的方式,因此這個時候咱們說從起點到其東南方向的點的權值更新值從6變成了5。這個時候咱們就完成了第一次鬆弛操做。
圖4:依舊是找距離起點最近的點。而後鬆弛咱們發現這個時候從起點到其東南方的點的耗費權值從5又變成了4.這個時候咱們完成了第二個鬆弛。
以後的方式同上:選定距離起點最近的點v。而後經過點v進行鬆弛操做。咱們發現可以經過增長走到目的地方式的複雜度(多轉彎)的方式咱們可以鬆弛掉權值,使得耗費的權值更小。
void Dij()//咱們這裏起點爲1號編碼點。咱們這裏的d[]表示從起點到這個點須要的權值。w[a][b]表示點a到點b這條邊的權值.
{
咱們這裏的d[]表示從起點到這個點須要的權值//加以強調其含義。
{
}
}
}
這個是在DP專題中,斐波那契數列:直接用線性遞推來求f[n]複雜度是O(n),更適合球全部的f[1]到f[n];若是用矩陣乘法算法的話計算K^n只有O(log n)的複雜度,只須要求出某個特定的f[n]是就佔優了;
數字乘矩陣:
將每一個位置的數字都與常數相乘
矩陣加矩陣:
將各個位置的數字相加
矩陣乘矩陣:
這個矩陣相乘怎麼理解看了別人的博客才懂的,借鑑博客地址在文章開頭;
具體的就是線性方程:
證實方法(借鑑):
老實說,從上面這種寫法,已經能看出矩陣乘法的規則了:係數矩陣第一行的2和1,各自與 x 和 y 的乘積之和,等於3。不過,這不算嚴格的證實,只是線性方程式轉爲矩陣的書寫規則。
下面纔是嚴格的證實。有三組未知數 x、y 和 t,其中 x 和 y 的關係以下。
x 和 t 的關係以下。
有了這兩組方程式,就能夠求 y 和 t 的關係。從矩陣來看,很顯然,只要把第二個矩陣代入第一個矩陣便可。
從方程式來看,也能夠把第二個方程組代入第一個方程組。
上面的方程組能夠整理成下面的形式。
最後那個矩陣等式,與前面的矩陣等式一對照,就會獲得下面的關係。
矩陣乘法的計算規則,從而獲得證實。
具體怎麼用吶:
用於遞推中,能夠一程度的減時;
例如:斐波那契數列,f[n]=f[n-1]+f[n-2];
f[0]=0;
f[1]=1;
用兩個矩陣模擬出這個遞推關係,就能夠了;