2014寒假專題訓練題解

2022-05-18 12:39:33 字數 3626 閱讀 7989

分治

分治法是自己一直以來不太熟悉也不重視的演算法,所以這次的題目做得也不好。

第二題roots,求一元三次方程的根(保留4位小數)。因為「二分法求方程的根」已經由數學老師在數學課上比較詳細地講解過,所以第乙個想到的就是這個方法。(後來同學說如果結果保留的小數字少一些比如只保留兩位小數時可以乘以100之後列舉,不過在這一題當然是行不通了。)但是實現起來卻是困難重重,最後只是看樣例過了就直接做下一題,明知有問題也不願多試幾組資料,所以測評的時候只拿到了極低的分數。後來改的時候也遇到了很多麻煩,主要問題是答案輸出(fixed和setprecision)、四捨五入(要先把得到的小數乘以10000,加上0.5後取整再除以10000)、判斷乙個double型資料是否等於0(印象中double型資料似乎不能直接和0比,但是實際上可以,應該是記錯了或者自己顧慮太多)。最後確定下來的方法大概就是在主函式裡從-100列舉到100,以相鄰兩個數為區間端點進行二分求根,如果左端點的值與右端點的值異號則說明有乙個根在該區間內,取區間中點計算函式值。若中點函式值與左端點函式值異號則在根左半區間,否則在右半區間,然後繼續遞迴,直到區間的端點已經足夠精確就可以輸出答案(至少應5位,為了保險起見我遞迴了19次即保留了19位小數,但是後來發現其實 double 的精度都不足 19 位)。還有對於特殊情況的處理,比如端點的函式值恰好為0,因為我是-100到100都呼叫求根函式所以有可能對於乙個恰好為方程解的整數端點輸出兩次,為此我設定了標記陣列以判斷乙個整數解是否已經輸出過(似乎有更好的方法就是在求根函式中對於區間的右端點函式值是0的情況不進行判斷,等到主函式中下一次迴圈時候的左端點自然就是目前的右端點,這樣的話還要考慮對100這個值再手動判斷一次)。最後花了不少時間勉強算是過了,自己感覺寫得很不乾脆,處理方法都有點拖泥帶水,不太滿意。

第三題reverse,求乙個無序序列中逆序對(序號嚴格遞增數值卻嚴格遞減的一對數)的個數。沒有思路,直接搜尋,即對於每乙個序列中的元素掃瞄它後面的元素,如果有比它小的元素則結果+1。結果是40分,應該說搜尋能騙到這麼多分也不錯了(而且這個搜尋似乎沒有優化的餘地)。因為題目是在分治法的專題裡,所以有往分治這方面想,但是想到的方法基本上複雜度和搜尋差不多,所以也就放棄了。標程用的方法是與歸併排序相結合的方法(果然是分治專題,分治的兩大經典例子歸併排序和快速排序都考到了……)。歸併排序的特點是合併兩個子串行時兩個子串行已經是有序的,而且左子串行的元素序號都小於右子串行,那麼如果左子串行中有乙個元素a大於右子串行的乙個元素b,則左子串行中a後面的元素必然都大於右子串行的b元素,則可以計算得出關於元素b的部分逆序對個數(即b前面的元素與b構成的逆序對,還有一部分是b與其後的元素構成的逆序對)。寫起來並不複雜但是因為從來沒有寫過歸併排序所以也就帶著抗拒情緒拖了幾天,終於在訓練的最後一天寫完了。寫完自己編了幾組資料,但是不知為何還是錯了,後來發現自己忘了把改過的**放到評測機收取**的資料夾裡了……血淋淋的教訓。

第四題mason,麥森數,題目描述沒什麼用,求2^p-1的位數與後500位的值。赤裸裸的高精度嘛。一開始想的確實是只要儲存後500位的值就夠了,但是不知道怎麼求2^p-1的位數,於是最後還是決定把2^p-1全部算出來。時間複雜度可想而知。而且由於對高精度乘法的不熟悉,寫高精度乘法用了不少時間。再經過同學提醒後解決了兩個問題:快速平方(自己好像還是第一次聽說這個演算法,即2p = (2p/2)2)和最後答案-1的問題(還以為要處理借位的問題,其實2^p的末位根本不可能是0)。60分。講解之後明白了不用求出完整的2^p,只要儲存最後500位,至於位數,其實可以直接根據p算出來的(w=p*lg2),做題時同學這樣告訴我,我還不太相信……看來以後要多注意運用數學知識對演算法進行優化。

做完這些題發現自己確實在分治法方面還太欠缺了。

動規 day1

動態規劃,我一直以來對其抱著敬畏之心的一種演算法(策略),終於在毫無思想準備的情況下與我碰撞了……其實早在做noip2002過河卒的時候就接觸到動規了(那時還是學長教我的記憶化搜尋),再到後來看書做掉數字三角形,再到暑假裡做lis、01揹包問題,其實自己不知不覺已經做了一些動規的題了,但是畢竟沒有正規地學習過,做到這些題目時自然就暴露了自己的真實水平了。

第一題pie,給定一些餡餅從天上掉落的時間與位置,人1秒內可以移動1個單位距離,求能接到的餡餅的最大數目。通過這道題我充分明白了自己的弱點:只會寫遞迴,不會寫遞推。遞迴的缺點很明顯,如果有記憶化搜尋的話(做的時候沒有意識到要用記憶化)時間倒是沒問題,但是當資料大的時候可能會導致系統棧溢位,而且如果狀態定義得不夠好,就會導致記憶化搜尋需要的記憶體空間太大(似乎遞推也需要注意這個問題)。做這題的時候沒有清晰的做動態規劃題的順序,所以也就沒怎麼注重狀態轉移方程的書寫,完全是憑藉感覺寫下了遞迴求解的程式。因為人在某個位置時面臨3種選擇:在原地待到下一秒、移動到左邊的位置與移動到右邊的位置。所以很快就可以寫出遞迴的函式,加上邊界的處理就可以了。寫完之後信心滿滿地開始做下一題了(因為這種方法看上去確實很正確)。測評結果出來當然很讓人失望:10分。其他點幾乎都超時。講解的時候發現標程的解法很巧妙:每個餡餅都對映到平面直角座標系的乙個點,掉落的時間為縱座標,位置為橫座標,這樣就轉化成了與基本的數字三角形問題非常相近的問題(只不過原來只能走左下或右下,現在還可以向下走)。這樣,每走一步,就移動到了下一層,即時間變化1。妙!

狀態轉移方程: s[i,j] = v[i,j]+max (s[i,j]表示在時間為j時站在i位置能得到的最大餡餅數 v[i,j]表示j時間在i位置掉下的餡餅數)

第二題charm,01揹包問題。這個不用多講,自己在暑假裡也已經做過了不少類似的簡單題,不過……自己還是忘了怎麼用遞推寫,於是繼續遞迴寫,而且又沒有用記憶化搜尋,結果當然是又大部分超時。用遞推寫再加上用滾動陣列後ac。萬惡的遞迴……

經典的01揹包狀態轉移方程: s[i,w] = max (s[i,w]表示把i件物品放入容量為w的揹包裡能得到的最大價值 c[i]表示第i件物品的費用 v[i]表示第i件物品的價值)

第三題separate,把乙個大數字n分成x部分,使得這x部分乘積最大。想了很久,我自己的想法是可以當做01揹包問題來做:設這個數字的長度為l,則一共有l-1個位置可以插入分隔線來分割這個數字,所以問題轉化為在這l-1條分隔線中取x-1條使得各部分乘積最大。赤裸裸的揹包問題啊。但是實現起來又比較麻煩。思路也許正確但是似乎效果不佳,初次評測0分,第二次也是0分……一直拖了兩天才ac。標程的方法不太一樣,狀態轉移方程見下。有個效果不錯的優化就是預先把數字n從第a位到第b位組成的數字算出來存到陣列裡,但問題是實現這個優化的方法……出於習慣我用stringstream,把數字字串的子串擷取出來輸出到stringstream裡然後再讀出到乙個long long的整數裡。但是我太高估了字串流的效率……後來之所以一直有4個點超時也是因為字串流效率低下。改成「乘十相加」之後快了不少。

狀態轉移方程: s[i,j] = max (a[i]!=b[j])

(a,b表示兩個序列 c[i,j]表示a[1..i]與b[1..j]的lcs長度)

動態規劃果然是神奇的演算法。

動規 day2

第一題buses,求行駛一定里程所需最小的車費,可以任意換車。

狀態轉移方程: m[i] = min (f[i]表示前i個元素的最長下降子串行長度,a即序列)

第四題run,求在n個城市之間來回飛k趟所需最小花費。題目很複雜,很容易滋生抗拒心理。經老師提醒想到最短路模型……但是複雜得多。最後自己用動規寫,發現其實和圖論演算法差不多。但是因為資料結構沒有設計好(用了乙個包含一維陣列與二維陣列的結構體陣列),導致後面查錯很麻煩,糾結了很久改了很多細節的地方才ac。

狀態轉移方程: f[i,j] = min{f[i-1,k] + m[k, j, i-1] | k

注意套用熟悉的模型。

GDUT 寒假訓練題解報告 專題II

題目 省自從實行了很多年的暢通工程計畫後,終於修建了很多路。不過路多了也不好,每次要從乙個城鎮到另乙個城鎮時,都有許多種道路方案可以選擇,而某些方案要比另一些方案行走的距離要短很多。這讓行人很困擾。現在,已知起點和終點,請你計算出要從起點到終點,最短需要行走多少距離。input 本題目包含多組資料,...

寒假訓練POJ 3268題解

一開始以為就是用多次最短路來求解,把各個奶牛到終點的最短距離算出來,再把終點到各個奶牛的最短距離算出來,計算每頭奶牛所用最短時間,輸出最大的,結果超時了,看了別人的題解才恍然大悟。因為前者需要執行多次最短路,後者只用一次就可以解決問題,由於前者的原因超時了。後者不變,需要改變求前者時間的辦法,因為是...

題解 牛客寒假訓練賽1

牛客寒假訓練賽1,自己水平還是太差了。a 題意 有計算符號和結果,求最初的數 思路 簡單模擬 include define ll long long struct note a 101 int main printf lld k return 0 b 給定2,0,4的數量,對其進行排列,計算1 i的...