量杯問題
credit to 明年校內賽要把我電爛的Hubert Chang
made and presented by yungyao
解題概念
(以圖論角度)
1.把一個狀態視為圖上的一個節點
2.我們已經知道初始的節點為全部杯子都為0的狀況 ,目標節點為包含目標水量的某點。
3. 找到目標水量。
How?
先把圖建出來
令一個節點為以下序列\([c_1,c_2,...,c_n]\),量杯大小為\([l_1,l_2,...,l_n]\)
我們可以發現:我們有一條邊\((s\to e)\)
若且唯若下方至少一條件成立
- \(s_{c_i} \neq l_i \text{ and } e_{c_i} = l_i\)
- \(s_{c_i} \neq 0 \text{ and } e_{c_i} = 0\)
- 選定一組 \((i,j)\) \(s.t.\) \(e_{c_i} = s_{c_i} - min(l_j - s_{c_j},s_{c_i})\text{ and }e_{c_j} = min(s_{c_j} + s_{c_i},l_j)\)
且\(\forall k \in [1,n]\setminus \{i,j\}, s_{c_k} = e_{c_k}\)成立
And
我們必須找到使 \(t \in u_c\) 成立的 \(u\) 中
距離起點最短的那個點
換句話說我們在求的是非帶權圖中的的單點源最短路
而這件事可以透過BFS達成
複雜度?
由於BFS的複雜度是 \(O(V+E)\)
而點的數量很顯然的是狀態數
而由前述的建圖方式可知以每個狀態為起點的邊
最多有\(n^2 - n + 2n = n^2+n\)個
另外,狀態數則很顯然會屬於\(O(m^n)\)
因此總複雜度為\(O(m^n + m^n(n^2+n)) = O(m^nn^2)\)
(\(n,m\)分別表示量杯數及最大量杯容量)
補充by 8e7
要注意一點是實際上的水量必為\(gcd(l)\)的倍數
再加上bfs不一定會完整跑完整張圖
因此上述複雜度分析僅是一個上界
(量杯問題是NP-Hard source)
實際運行速度跟更精確的複雜度可能會更快
Really?
前述複雜度分析中
我們其實假設了每次檢查一個點是否被走訪都是\(O(1)\)
但實際上卻不一定會是這樣,因此若令檢查的複雜度為\(O(w)\)
則複雜度上界應為\(O(m^nn^2w)\)
因此若要求實際上的複雜度,我們現在必須考慮\(w\)的值
hash table優化
上禮拜賴昭勳教過的,我想說練習看看。
這句by hubert,他裝弱
有一種直觀的想法是逐一檢查已經訪問過的點看是否有重複
但這樣的複雜度將會是線性時間
而我們可以利用hash table的方式
將複雜度降到\(O(1)\)(常數時間)
作法:
1.把一個狀態\((c_1,c_2,c_3,c_4,c_5\))代入hash function (以下使用rolling hash,但其實任意一種hash方式都可,如後面我會使用bitwise的作法)
\(h(c) = \sum\limits_{i=1}^{5}c_i \times {p_1}^{5 - i}, p_1 \text{ is prime}\)
2.把\(h(c)\)去模另一個質數\(p_2\)(\(p_2\)必須夠大,以確保collision的機率夠低)
3.模完可以確保每個狀態都有對應的index
若有collision怎麼辦?
1. linear probing:
2. linked list:
Or
回溯解
回溯過程
由於我們在回溯BFS的過程
因此只要很簡單地維護每個節點在BFS Tree上的母節點就好
btw
由於你會發現這題狀態數只到\(51^5 < 2^{31}\)
所以你可以把整個狀態以一個int表示
你的hash可以用位元來寫,會滿好寫的
(當然hash也不是什麼難寫的東西,可是位元的常數很好)
但這題也沒有卡常(他卡一個酷酷的數學東西要解掉)
我實測用STL,pb_ds,位元hash都能AC掉
在TIOJ上運行時間:位元 \(<\) STL \(\approx\) pb_ds
雖然說pb_ds理論上要比STL快
但在沒有collision的狀態下,兩者的運行速度甚至是STL稍贏
Code (回溯解版本)
Top Coder
位元映射加上神奇IO優化,可以使你獲得Top Coder><
量杯問題
By yungyao
量杯問題
- 184