我的做題筆記 (轉)

amyz發表於2007-10-17
我的做題筆記 (轉)[@more@]

UVA 100 - The 3n + 1 problem - 0:05.107 316K

TLE 4次 WA 1次 AC n次
看了OIBH的介紹,我當初是決定用模擬+記憶化的方法來做,不過連續2次TLE了。後來改用了直接模擬不記憶的方法,結果還是TLE,真是百思不得其解。Bamboo說我應該得WA的,因為會有i>j的情況。我想想是該考慮上這種情況,又寫了。這下子好了,不是TLE而是WA了,原來是輸出的時候把後的i和j輸出了,我這才知道為什麼Bamboo的程式要先輸出i和j再做交換。真笨!!

錯誤總結:
之前的TLE,估計是因為沒有考慮細密,忽略了i>j的情況。WA是因為輸出的時候也沒有考慮好。如果用上記憶化,可能速度會快上一點。

UVA 101 - The Blocks Problem 0:00.040 64K

TLE 2次 PE 1次 AC 2次
這次錯在沒有好好讀題目,沒有處理好輸入不合法的情況,結果陷入死迴圈導致TLE(我發現UVA的TLE似乎都是死迴圈或是沒考慮周密情況而發生的)。
PE是受了樣例輸出的HTML的誤導。程式寫得非常複雜,第一次的程式(101.pas)有3K,不過思路是可以看得很清楚的。第二次(101_2.pas)修改到2K後,思路有點看不清了,但核心就是把各段程式碼中重複的部分合起來,多次的同形式內容寫成過程()

錯誤總結:
沒有認真讀題。

UVA 102 - Ecological Bin Packing - 0:00.555 316K

AC 1次
1次AC,挺高興的。不過這道題目也沒有什麼難度。技巧方面有幾個:
1.給定的資料讀入時是按B G C的順序,但是要求輸出的時候,序列應按字母順序排序,為了避免排序,在列舉的時候,就按B C G的順序進行。所以資料讀入部分,將讀入的資料以B C G的順序存放。
2.計算需要的移動數目時,不是一個個加,而是用總數減。如果箱子A專門放顏色X,則這個箱子中的顏色X肯定不用移動,其它的兩種顏色就要移動到別的箱子中去。靠這個,我們列舉i,j,k,表示三個箱子分別留下哪種顏色不移動(即對應了這個箱子的顏色),然後計算差值,即可簡單算出需要移動的數目。
3.由於i,j,k和肯定是6,k便可以不用列舉,而是用k:=6-i-j來計算。
4.為了方便記錄i,j,k,用一個陣列來儲存,這樣就可以整陣列賦值。

最後是老想昏頭的問題:能不能不改輸入的B G C順序,而是在列舉和計算的地方下手?我想是不行的,如果不把兩者統一起來,在計算的時候就會混亂。

UVA 103 - Stacking Boxes - 0:00.008 64K

WA 15次 OLE 3 次 AC n次
絕對是一個教訓!這是一道DP題,方法我已經有了,是starfish告訴我的。然而我幾乎是抄他的程式,結果卻是WA。我檢查了半天,懷疑了所有的部分,都沒有查出為什麼。最後重寫了一遍,竟然就OK了!複查的時候,我把程式段分別替換,竟然都還是WA!最後!!!!!我才發現竟然是宣告部分寫錯了,導致陣列越界!而UVA的給出的卻是WA!害死我了!!!!!!

錯誤總結:
檢查的時候不要漏檢查宣告部分,要在程式中用編譯開關開啟陣列邊界檢查!

URAL 1000 - A+B Problem - 0.02 sec 389K
?id=1000">
AC 1次
無話可說!

URAL 1005 - Stone pile - 0.07 sec 504K

AC 1次
這道題有兩種作法。
1.回溯法
由於N比較少(N<20),我們可以設有兩堆石塊A,B。每個石塊有兩種狀態:放在A,放在B。只要回溯列舉,算出A與B的差的絕對值,記下最小的就可以了。

2.DP
我們也可以設f[n,k]表示用前n個數是否可以算出k。我們可以得出狀態轉移方程:
f[n,k] = f[n-1,k-Wn] OR f[n-1,Wn-k] OR f[n-1,k+Wn]
這樣, 我們用兩個陣列進行翻滾就可以了

URAL 1009 - K-based numbers. - 0.02 sec 393K

AC 1次
這道題也有兩種作法.
1.列舉法
由題目條件2 <= K <= 10; 2 <= N; 4 <= N+K <= 18,我們可以推算出N<=8,數量並不算大,只要生列舉也是可以的。

2.遞推法
由於題目只要求計數,我們便考慮是否可以採用遞推、DP、組合數學的方法來計算。
我們可以這麼考慮:
假設f[n,1]表示的是首位為0的“合法”的n位K進位制數的個數,f[n,2]表示的是首位不為0的合法的n位K進位制數。
這樣,我們可以馬上得到邊界條件:
f[1,1] = 1 (一個0就是一個數)
f[1,2] = K-1 (K進位制是從0..K-1共K個數,除去0,就只有K-1個了)

我們每次在最左邊加上一個數字,然後我們可以寫出下列的遞推公式:
f[n,1] = f[n-1,2] (由於不能同時出現兩個0,所以在首位不為0的數前面加上一個0,就是這一類數的個數)
f[n,2] = f[n-1,1]*(K-1) + f[n-1,2]*(K-1) (在首位為0的數前面加上一個不為0的數字,在首位不為0的數前面再加上一個不為0的數字,就是這一類數的個數)

現在已經解決了這個問題,時間複雜度為O(N),空間複雜度為O(2N)。

不過還有的餘地。我們經過觀察,發現由第一個遞推關係,我們又可得f[n-1,1]=f[n-2,2],所以第二個遞推關係可以寫成:
f[n,2] = f[n-2,2]*(K-1) + f[n-1,2]*(K-1)

這樣我們就可以省去一個維,寫成:
f[n] = f[n-2]*(K-1) + f[n-1]*(K-1)

邊界條件也要隨之改變:f[1,1] = f[0,2] = 1
總之,邊界條件就是
f[0] = 1
f[1] = K-1
注意:如果我們直接從定義去理解,f[0]是想不出來的,這就是遞推的一個特點,要用公式去變換來找到具體值。

這樣,我們就把空間複雜度降為了O(N)。不過還能進一步再最佳化。我們發現f[n]只與f[n-2]和f[n-1]有關,也就是說,我們只需要儲存三個值就足夠了。
我們可以用f0,f1,f2來儲存,然後手工賦值翻滾,不過這樣太麻煩了。我們還有更好的辦法來實現:用MOD,寫成這樣:
f[n MOD 3] = f[(n-1) MOD 3]*(K-1) + f[(n-2) MOD 3]*(K-1)

這樣,我們只用定義一個陣列,翻滾的操作就不需要我們來手工完成了。空間複雜度也降為了O(1)。

另外兩道題URAL 1012和URAL 1013和這道題完全一樣,只是資料變大了,只能使用遞推來做,而且必須使用高精度計算。

URAL 1014 - The Product of Digits - 0.03 sec 393K - 2002.12.14

WA 2次 AC 1次
這道題也比較簡單,考的是你思維的嚴密程度。先來分析演算法:
題目給出一個數N,要求出一個數Q,其各位數字的乘積正好等於N。如果把N寫成N=a1*a2*a3*...*an(a1>=a2>=a3>=...>=an),則Q=a1a2a3...an。又可得知,0<=ai<=9(i=1,2,3,...,n),進一步,ai=0,1都不能取(取0乘積是0,取1乘了也白乘),所以2<=ai<=9(i=1,2,3,...,n)。我們就可以得出演算法了:將N分解為幾個2~9的因數的乘積,統計個數,從從大到小輸出相應個數的因數,就是所要求的數Q。顯然,分解的順序應是從大到小,這樣分解出的因數個數是最少的,數Q的長度是最短的,數Q才是最小的。如果N不能被完全分解,即分解完成後N<>1,則說明不存在這樣的數Q,就輸出-1。

好了,演算法是很簡單的,但是……題目有以下兩個陷井:
1.當N=1的時候,按我們的演算法,會沒有輸出,這時應特別處理,直接輸出1。我在做的時候,把N<10的情況都一起處理為直接輸出N。
2.當N=0的時候,如果不注意,你會認為應該輸出0。不過注意看題目:find the minimal positive integer,要求數Q為正數!所以應該輸出的是10!

最後該說吐血的事了:我WA了兩次都不是因為踏中上面的這兩個陷井,而是被HTML的編碼所害!如果用簡體中文來看這道題,不存在合條件的數時應該輸出的是"?",事實上!用ISO來看的時候,會發現那個"?"其實是"-1"!吐血吧?和UVA 103一樣,又是一個教訓!

錯誤總結:
看題目的時候,一定要把編碼切換成ISO。


-----------------------------------------------------
附:我以後做新的題目,就直接修改這個,而不再另開新文章了:)

2002.12.14 - 今天改了一下URAL 1009,加了最後一行:)還有做了URAL 1014。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-977250/,如需轉載,請註明出處,否則將追究法律責任。

相關文章