Problem
給予一張有向森林圖,要求任兩點若有一條路徑,則兩點不可著相同顏色,請問最少著色數為何?
Sample Input
|
|
Sample Output
|
|
Solution
明顯地,最長路徑長度若為 $L$,至少需要 $L$ 個顏色,因此針對每一個樹根進行最長路徑搜索即可,又由於題目給定森林,把每一個樹根抓出來取最大值即可。複雜度 $\mathcal{O}(N)$
|
|
給予一張有向森林圖,要求任兩點若有一條路徑,則兩點不可著相同顏色,請問最少著色數為何?
|
|
|
|
明顯地,最長路徑長度若為 $L$,至少需要 $L$ 個顏色,因此針對每一個樹根進行最長路徑搜索即可,又由於題目給定森林,把每一個樹根抓出來取最大值即可。複雜度 $\mathcal{O}(N)$
|
|
還記得孟德爾遺傳法則嗎?從血型分成 A, B, AB, O 型四種,由三個遺傳因子 A, B, i 兩兩組合而成。而外星人突破孟德爾遺傳法則,由 $N$ 個遺傳因子 (不包含隱性因子 i) 決定表徵,並且一個子代會從 $N$ 個父代分別繼承一個遺傳因子。
現在給予在場 $N$ 個父代分別有哪些遺傳因子 (若沒有告知,則剩餘都是隱性因子 i),接下來會有 $Q$ 組詢問,問某一個遺傳因子組合是否可以被在場 $N$ 個父代組合而成。
|
|
|
|
每一個父代都提供一個遺傳因子,問最後能不能滿足所有匹配,顯而易見地是一題完美二分匹配。建造 source - 父代 - 遺傳因子 - sink
。若某一組詢問需要父代提供遺傳因子 $x$,就去找尋有哪些父代可以提供遺傳因子,並且拉一條邊 父代 - 遺傳因子 x
流量為 1。隱性提供者特別判斷一下即可。
|
|
這一場是凌晨的比賽,因此沒有興趣參與,還是來翻譯一下吧!
要製作回力鏢的雙翼,需要兩個對稱形狀的翼構成,簡單來說是兩個相同字串。給予左翼 $L$ 和右翼 $R$,現在 A 和另一名小夥伴 B 同時更改左翼 $L$,使得 $L = R$,請問最少時間為何。
每一時刻,$A$ 可以選擇 $[0, x]$ 都改變成顏色 $c_1$,而 $B$ 則可以選擇 $[y, n-1]$ 全部變成顏色 $c_2$,要求 $A$ 和 $B$ 的塗色區間不可重疊,意即 $y > x$。
由於每一次塗色是前綴或者後綴,那對於 A 而言,一定是選擇 $x$ 嚴格遞減,反之對於 $y$ 是嚴格遞增,否則之前的操作會白工。藉由上述推測得知,A 和 B 塗色的區間不會重疊,得到最後一定要求左區段和右區段工作時間最大值。
目標找到 A 完成左區段 dpL[0...x]
的最少時間,以及 B 完成 dpR[y...n-1]
的最少時間,最後答案為 min(max(dpL[0...x], dpR[x+1...n-1]))
。計算 dpL[]
和 dpR[]
的方法是一樣的,左右相反即可,
由於只能更改左翼,右翼不能更改,那麼一旦改了左翼位置 $x$,事實上要全部變動成右翼,變數個數 $d$ 即是右翼不同的連續相同字符片段。根據貪心策略,若左翼位置 $x$ 和右翼位置 $x$ 相同,則變動費用為 dpL[x] = dpL[x-1]
,否則就是 dpL[x] = d
。時間複雜度 $\mathcal{O}(n)$。
參與一場遊戲,獎品無限多個,遊戲規則要求一次投擲 $n$ 硬幣,硬幣出現正面的機率 $P$,只要出現 $K$ 個以上正面即可獲得一份獎品,現在手裡握有 $N$ 個硬幣,你可以邀請很多個小夥伴幫忙參與遊戲,將這 $N$ 枚硬幣分給小夥伴玩。在最佳策略下,請問獲得獎品個數的期望值為何?
現在的目標要找到怎麼分,小夥伴個數不是問題,每一個小夥伴要拿多少枚硬幣是主要問題。定義 dp[i][j]
為投擲 $i$ 枚硬幣恰好 $j$ 枚正面的機率,遞推得到 dp[i][j] = dp[i-1][j-1]*P + dp[i-1][j]*(1-P)
。由於大於等於 $K$ 枚只能獲得一份獎品,定義 dpw[i]
為投擲 $i$ 枚硬幣獲得一份獎品的期望值,遞推得到 dpw[i] = sum(dp[i][j]), j >= K
。
最後,定義 dpw[i]
為分配 $i$ 枚硬幣給小夥伴的最大期望值個數,得到 dpw[i] = max(dpw[i-j]+dpv[j])
。每一步都是 $\mathcal{O}(N^2)$,時間複雜度 $\mathcal{O}(N^2)$。
有一個奇怪的收藏家,收集很多梯子,每一個梯子位於水平位置 $x_i$ 高度為 $H_i$,然而當地特有蛇種喜歡懸掛在兩個等高 $H_p$ 的梯子之間,並且兩個梯子中間的其餘梯子都滿足 $H_r \le H_p$。若蛇懸掛在兩個距離 $L$ 的梯子之間,則需要餵養 $L^2$ 單位的飼料,請問飼主一天要餵多少單位的飼料。
搭配組合的數學計算,由於懸掛的組合保證中間不會有高於兩側,則可以用單調堆完成紀錄,由左而右依序加入梯子,在單調堆中維護梯子高度遞減的位置。由於等高的梯子個數需要合併,否則沒辦法計算 $L$,為了計算 $L^2$。當從左而右加入梯子時,往左找到合法的等高梯子,由於得知 $\sum (X - x_i)^2$,把式子展開得到 $nX^2 - 2X \sum x_i + \sum x_i^2$,因此需要對於每一個高度,要合併 $\sum x_i$ 和 $\sum x_i^2$。
由於每一個元素至多 push 和 pop 一次,時間複雜度 $\mathcal{O}(N)$。
在一棵 $N$ 個節點的無根樹,每一個節點可填入 $K$ 種顏色,對於每一個節點填不同顏色都有不同花費,若一個節點 $u$ 的鄰居中有兩個以上節點顏色相同,則 $u$ 需要額外花費 $P$ 來維持平衡。請問著色的最少花費為何。
若不考慮額外花費 $P$ 這一點,這題就是再簡單不過的貪心。從鴿籠原理知道,若一個點的鄰居大於 $K$ 個,則他必然存在兩個相同顏色的鄰居,一定需要花費 $P$ 維持平衡。由於圖給定一棵樹,自然而然地就設想到樹形 dp,維護子樹的最少花費進行合併。
由於子樹的根著色不用考慮,但方案合併要考慮父節點要著色的方案,因此得到狀態 dp[u][c1][c2]
節點 $u$ 著色 $c_1$,$u$ 的父節點著色 $c_2$。分成兩種狀態轉移,是否要花費 $P$,若 $u$ 花費 $P$,直接合併子樹 $v$ 的最小值 dp[v][?][c1]
。若不花費 $P$,勢必要讓子樹都填入不同顏色,由於每一個節點配對顏色花費都不同,最小化著色方案是標準的二分圖帶權匹配,可以用 KM 算法或者是最少費用流完成,只要排除預設 $u$ 的父節點顏色建圖即可,幸好不花費的數值都小於等於 $K$,KM 算法提供 $\mathcal{O}(K^3)$。整體的時間複雜度最慘 $\mathcal{O}(N \times K^2 \times K^3)$。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在此特別感謝 lwc (Wei Chen Liao) 提供思路。
手動暴力打表後,上網蒐到 A125714 Alfred Moessner’s factorial triangle. 恰好是這個數列的答案。
由於每一個人的籤不重複,若把他抽到的籤當作指向出去的邊,那麼保證這一個點恰好一個出邊和一個入邊。當所有籤抽滿後,圖看起來是好幾個環。
遞迴定義 dp[i][j]
表示有 $i$ 個節點和 $j$ 個環。考慮新加入得點要抽到哪一個籤,加入到 $j$ 個環的情況,則是把某一個環的某一個位置打開加入,或者自身形成一個環,最後得到 dp[i][j] = dp[i-1][j-1] + (i-1)*dp[i-1][j]
|
|
讀入一個 regex,一個主字串 $s$,請問有不同的 $i$ 滿足 $s$ 的子字串 s[j...i]
匹配 regex。
正規表達式長度最多 500,主字串 $s$ 長度最多 $10^7$。
|
|
|
|
隔了一年才解決它,這一題很明顯地在編譯器的範疇,要將一個 regex 轉換成 NFA 甚至 DFA。一開始設想轉換成 DFA,但長度 500 轉換成 DFA,用狀態壓縮的方式狀態增長非常大,不知道是不是指數成長,上傳就得到 Runtime Error。
將 regex 轉換成 NFA (nondeterministic finite automata) 並不難,但節點會很多且存在很多 $a \overset{\varepsilon}{\rightarrow} b$ 這種類型的邊。在解析 regular expression 時,用線性 $\mathcal{O}(n)$ 或者是 $\mathcal{O}(n^2)$ 都沒關係。因為在計算答案至少 $\mathcal{O}(10^7)$。
得到最原生的 NFA 後,接下來要思考怎麼找到匹配的子字串 $s_{j, i}$,維護可轉移的狀態集合 $S$,若當前 $S$ 中存在 acceptable 狀態,則找到一個 $i$ 解。依序加入字符 $s_i$,維護 $S$ 的做法如下:
上面的算法並沒有錯,但原生的 NFA 產生方式在計算閉包時很慢,過多的邊和重複狀態導致。因此需要重建邊,預處理每一個字符轉移,移除所有 $a \overset{\varepsilon}{\rightarrow} b$,這麼一來就能在時限內通過。時間複雜度 $\mathcal{O}(|s| \times (\text{state} + \text{edge}))$
|
|
現在的系統架構中,內存 (Memory,台灣翻譯:記憶體,大陸翻譯:內存) 的大小仍無法完全像硬碟機一樣。如檔案大小 64GB,內存只有 4GB,處理檔案無法全部 In Memory,當然最近的硬體技術也逐漸朝著 All In Memory 的方式進行加速。回過頭來,在內存不足的情況下,來練習如何處理檔案大小遠大於內存的情況吧。
給予一個 binary file 的檔案名稱,裡面用 4 個位元組為一個 signed 32-bit 整數,有數個直到 EOF
,請排序後以 binary mode 導入 stdout 輸出。
標準輸入串流只有一行一個字串,表示檔案名稱 $F$。檔案大小最多 16 MB,而你的程序被限制最多使用 4 MB 的內存。
每個整數 $x$,限制條件 $0 \le x \le 10^9$
輸出在標準串流以 binary mode 下,請避開使用 console 輸出,會因為特殊字元 (如 「嗶」一聲) 導致系統嚴重當機。
|
|
|
|
download p10068-in.dat
|
|
1.dat
以 binary file 儲存 10 個 4 bytes 整數,依序為 5, 9, 3, 5, 0, 1, 1, 8, 4, 1,排序後即為 0, 1, 1, 1, 3, 4, 5, 5, 8, 9。
由於限制內存使用量,無法寫入暫存檔案,可利用多次讀檔完成。
乍看之下,這一題是最常見的 external sort (外部排序),外部排序需要額外寫檔案,由於記憶體用量計算,一寫檔案會計算到使用的記憶體中,實際上這一題不寫檔也是能解決的。
<某整數, 某整數出現個數>
|
|
現在的系統架構中,內存 (Memory,台灣翻譯:記憶體,大陸翻譯:內存) 的大小仍無法完全像硬碟機一樣。如檔案大小 64GB,內存只有 4GB,處理檔案無法全部 In Memory,當然最近的硬體技術也逐漸朝著 All In Memory 的方式進行加速。回過頭來,在內存不足的情況下,來練習如何處理檔案大小遠大於內存的情況吧。
給予一個 binary file 的檔案名稱,裡面用 4 個位元組為一個 signed 32-bits 整數,有數個直到 EOF
,保證恰好有奇數個,請輸出中位數為何?
標準輸入串流只有一行一個字串,表示檔案名稱 $F$。檔案大小最多 16 MB,而你的程序被限制最多使用 4 MB 的內存。
每個整數 $x$,限制條件 $0 \le x \le 10^9$
輸出一行整數 $Median$ 為 binary file 的中位數。
|
|
|
|
二分搜尋,分桶
download p10067-in.dat
|
|
這一個問題很久以前見過,對岸通常叫做「海量資料找中位數」約束在記憶體不夠的情況下,如何高效率找到中位數,操作時允許重複讀檔。
遲遲沒有 Online Judge 進行記憶體用量檢測以及開放讀寫檔案,如今有機會擔任台灣大學計算機程式設計的課程助教,架了一個允許亂來的 Online Judge - Judge Girl (中文翻譯:批改娘系統),讀寫檔案也不會被封鎖!洽詢 網站,目前只針對課堂學生開放。
回到問題,題目給訂 16MB 的整數序列,最多 $n = 4 \times 10^6$,由於記憶體不足沒辦法直接宣告陣列大小為 $n$ 的整數陣列,又由於數字可能會重複,就沒辦法利用 bitmask 進行壓縮,只能倚靠不斷地讀檔案進行計算。若以數值範圍 $[0, V]$,大致放分成兩種策略
讀取檔案時,在有限記憶體空間,配置一塊作為緩衝區,一次讀入一坨子序列,一個一個讀取效率非常差,別輕忽從磁碟讀取資料的速度。剩餘空間再進行紀錄用途。從效能比較 二分搜尋 慢於 分桶算法。
二分答案 $x$,接著線性掃描序列,計算有多少個整數小於等於 $x$,藉此找到中位數。
|
|
分桶算法分成兩次掃瞄,假設內存分配最多 $I$ 個 32bits 整數 ($I \ll N$)。
[0, V/I-1]
、[V/I, 2V/I-1]
、… 等,計算區間範圍內的個數,讀取一次檔案,可以得到中位數介於 [iV/I, (i+1)V/I-1]
。
|
|
給 $N$ 個整數序列,每一個整數表示題目難度,可以在序列中插入整數,目標在序列中依序挑出 4 個整數作為比賽題目配置,並且要求不改變原本序列順序,滿足題目難度嚴格遞增,相鄰題目難度不大於 10,請問至少加入幾道題目。 (意即要插入題目的數量使得序列長度被 4 整除。)
最保守的方式 DP,只需要 $\mathcal{O}(N)$ 時間,記錄狀態 dp[i]
表示前 i 個 (不含) 整數滿足前述要求至少要插入幾個數,得到
,計算 $c_{j, i}$ 列出 $A_j, A_{j+1}, ..., A_{i}$ 得知至少要插入幾個數,貪心計算兩兩數字之間的差值 $\textit{diff}_k$,$c_{j, i} = \sum (\left \lceil \frac{\textit{diff}_k}{10} \right \rceil - 1)$。每一個轉移最多 4 次,故時間複雜度 $\mathcal{O}(N)$。
小夥伴有 $L$ 件未洗衣物,洗衣店有 $N$ 台洗衣機和 $M$ 台烘衣機,洗衣機和烘衣機一次只能處理一件衣物,而對於每一台洗衣機對於衣物都有不同的處理時間 $W_i$,烘衣機則固定每 $D$ 時間就能烘好一次衣物。小夥伴可以暫存洗好衣物的籃子無限大,同時移動衣物的時間快到可忽略,請問至少要隔多久才能帶著所有洗好烘好的衣物離開洗衣店。
時間軸模擬題,用 priority queue 維護時間軸,在每一個時間戳記下檢查事件發生。模擬兩台機器交換衣物會稍微複雜,事件定義有三種:
時間複雜度 $\mathcal{O}(L \log (N+M))$。
宅宅的退休工程師想建造遊艇,預算總額從 $[A, B]$ 隨機地挑一個,請問建造所剩餘金額的期望值為何。建造策略如下:
數學期望值計算,時間複雜度 $\mathcal{O}(N)$,窮舉在每一個步驟罷工,因為卡在區間 $[A, B]$,計算剩餘金額在此步驟停止會稍微複雜。一艘船需要的預算為 $C$,那麼在某些情況 $C_i$ 會完全被 $[A, B]$ 包含,累計期望值 $\frac{0+C_i}{2}$,接著針對部分在區間 $[A, B]$ 的邊界情況。計算方法很多,在此也不多作解釋。
dp[參與選手集合][勝者] = 1/0
記錄子樹的情況,只需要 $\mathcal{O}(2^{16} \times 16)$ 個狀態總數,在集合合併處理需要窮舉子集合,由於我們只需要窮舉特定集合大小,複雜度不會到 $\mathcal{O}(3^{16})$ 這麼慘。合併時根據兩子樹的勝者相對打,同時紀錄兩位可夠晉級的最大最小回合數即可。
|
|
|
|
|
|
|
|
好一陣子沒用 C++ 解題,感覺新鮮。嗯 …
|
|
給予在星空下每顆星的座標,找出由三點構成回力鏢樣貌的組合個數,需滿足一點到兩點距離相等。
窮舉所有可能 $\mathcal{O}(N^3)$ 將會 TLE。窮舉回力鏢中心座標 $O$,統計其餘點到 $O$ 的距離,對於相同距離計數,套用組合公式 $C(n, 2)$ 加總,時間複雜度 $\mathcal{O}(n^2 \log n)$
給予只有兩排的長形棋盤狀的營地,雇用最少警衛監視每一塊區域,有些區域會因遮蔽物擋住警衛視線,而警衛只能監視平行兩軸的連續相鄰格子。求最少警衛個數。
第一眼覺得是 flow 可解,但求最少的模型建不太出來 Orz,就決定貪心。在同一行,連續片段上必然放置一個警衛,優先將連續片段總數匹配另一行連續個數恰好為一,剩下的情況再利用連續個數恰好一個的相互匹配。
參與一場遊戲,獎品價值為 $P$,給訂 $N$ 個正整數序列,挑選連續的序列總和小於等於 $P$ 即可獲得獎品,請問有多少挑選方式。
由於是序列為正整數,前綴和具有單調性,滑動窗口統計方法數只需要 $\mathcal{O}(N)$。最懶得方式是直接二分搜尋 $\mathcal{O}(n \log n)$。
從 $N$ 個不同的單字中,打印恰好 $K$ 個輸出在螢幕上,操作有三種 1. 在尾端插入一個字符 2. 刪除字串尾端一個字符 3. 將緩衝區的字串印出來,並且在操作結束後,緩衝區大小為 0。求最少操作個數為何?
第一次遐想,由於要最少操作個數,想必前綴相似的都必須連續,因此先對所有單字排序,接著按照順序 DP。先不考慮操作 3,最後一定恰好為 $K$ 次,只需要考慮插入和刪除操作,維護最後一個在緩衝區為單字 $i$,轉移到下一個單字 $j$ 的成本是 len(Wi) + len(Wj) - LCP[i, j]*2
(刪除前面 $W_i$ 的後綴再加入 $W_j$ 的後綴),因此要預處理 LCA[i, j]
,題目給訂範圍應該不用套用 Trie 或者是 Suffix Array 進行查找,暴力計算即可。定義 DP[i][k]
表示目前打了 $k$ 個單字,最後一個單字恰好為 $i$ 的最少操作次數。在 DP 部分,時間複雜度 $\mathcal{O}(N^2 K)$。
|
|
|
|
|
|
|
|
給予一個字串 $S$,求出所有子字串的集合,將集合內除了空字串以外的字串照字典順序輸出。
|
|
|
|
新手上路練習題,沒打算考很難的操作,即便如此信任也破產,看來出成變態題的機會高一點導致釣不到人來寫。
直觀作法是窮舉所有子字串,去掉重複即可,時間複雜度 $O(N^3)$,由於 $N = 500$ 也要考慮輸出的成本,所以不可能出太大。儘管如此,來比較算法之間的空間和時間用量。
首先,最常見到的 set 作法,若直接儲存字串空間用量 $O(N^3)$,為了避免空間太多,可以自己寫一個 compare function 來完成,因此 set 只要記錄子字串的起始位置和長度即可,時間複雜度 $O(N^3)$,空間降到 $O(N^2)$。
接著,進入到後期,常用到字典樹 trie,若把所有後綴插入到字典中,接著在 trie 走訪輸出所有結果即可,時間複雜度 $O(N^2)$,空間 $O(N^2 \times 26)$。可以使用 double-array trie 捨棄掉一點時間,降低節點的空間使用量。
最後,比較強悍的後綴自動機,在劉汝佳的書上主要是用 DAWG (directed acyclic word graph) 來描述 suffix automaton,後綴自動機可以在線構造,時間和空間複雜度都是 $O(N)$,後綴自動機可以接受 $S$ 所有的後綴,關於建造時間和狀態總數的證明可以參考 《Suffix Automaton 杭州外国语学校 陈立杰》 的簡報。
若不想這麼詳細的證明狀態總數和時間複雜度,可以從 AC 自動機的建構概念來思考,一樣有 fail 指針,來維護當一個後綴失去匹配,移除前綴要移動到的狀態。特別的是,每一次增加最多兩個節點,最後一次增加的節點為 accept state (後綴自動機只有一個或兩個 accept state),其中一個節點是解決當前字串的後綴長度 1 的轉移。
根據這一題的需求,走訪一次後綴自動機就能印出所有子字串。
|
|
|
|
|
|
|
|
|
|