量杯問題

張衝和好爛

解題概念

(以圖論角度)

1.把一個狀態視為圖上的一個節點

 

2.我們已經知道初始的節點為全部杯子都為0的狀況 ,目標節點為包含目標水量的某點。

 

3. 找到目標水量。

How?

先把圖建出來

令一個節點為以下序列\([c_1,c_2,...,c_n]\),量杯大小為\([l_1,l_2,...,l_n]\)

我們可以發現:我們有一條邊\((s\to e)\)

若且唯若下方至少一條件成立

  1. \(s_{c_i} \neq l_i \text{ and } e_{c_i} = l_i\)
  2. \(s_{c_i} \neq 0 \text{ and } e_{c_i} = 0\)
  3. 選定一組 \((i,j)\)  \(s.t.\) \(e_{c_i} = s_{c_i} - min(s_{c_i},l_j - s_{c_j})\text{ and }e_{c_j} = min(l_j,s_{c_j} + s_{c_i})\)

且\(\forall k \in [1,n]\text{ and }k \notin \{i,j\}, s_{c_k} = e_{c_k}\)成立

And

我們必須找到使 \(t \in u_c\) 成立的 \(u\) 中

距離起點最短的那個點

換句話說我們在求的是非帶權圖中的的單點源最短路

而這件事可以透過BFS達成

my slides on BFS(and basic graph theory)

複雜度?

由於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\)分別表示量杯數及最大量杯容量)

Really?

前述複雜度分析中

我們其實假設了每次檢查一個點是否被走訪都是\(O(1)\)

但實際上卻不一定會是這樣,因此若令檢查的複雜度為\(O(w)\)

則複雜度應為\(O(m^nn^2w)\)

因此若要求實際上的複雜度,我們現在必須考慮\(w\)的值

hash table優化

上禮拜賴昭勳教過的,我想說練習看看。

每次加入節點都要用線性搜尋檢查其是否出現過,這樣每次都是O(n)。若改用hash table,如果有做好的話,幾乎都可以O(1)搜到。

作法:

1.把一個狀態(a,b,c,d,e)代入某

f(a,b,c,d,e) = a*p1^5+b*p1^4 + c*p1^3 + d*p1^2 + e*p1^1 ,p1為某質數

 

2.把f(a,b,c,d,e)去模另一個質數p2(hash_table的大小)

 

3.模完可以確保每個狀態都有對應的index

 

若有collision怎麼辦?

1. linear probing:

 

 

2. linked list:

 

Or

unordered_map

C++ 的 STL幫你解決實作ㄌ(不過hash function要自己寫)

或者你想要更快的話

pb_ds hash table

回溯解

回溯過程

由於我們在回溯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稍贏

量杯問題papabeard

By Hubert chang

量杯問題papabeard

  • 50