[NOIp 2015] 對D1T2的一些拓展研究

<del>隨便口胡,錯誤一大堆,歡迎打臉</del>

D1T2因爲只有n條邊,所以只存在簡單環,那我們把這個問題複雜一下,每個人可能能告訴多個人,也就是邊的數量大於n,那麼這樣的話就會出現複雜環了。如果還是直接用tarjan求的話肯定是錯的,因爲tarjan每次找的是一整個大環。在這裏想到了一種比較簡單的解法。

我們每次選一個沒有走過的點開始DFS,並一路將走到的點壓入存路徑的棧中,同時用visit[i]表示i有沒有走過,設當前的路徑是p,

Visited[i]=0 …………i從未被訪問過 精確的說是i可以訪問

Visited[i]=1 …………i已經被訪問過 且i在p中

Visited[i]=2 …………i已經被訪問過 且i不在p中

對於所有visited[i]=2的點我們就沒有必要訪問了,說明:設當前點是now,下一個是next且visited[next]=2,假設next和now在一個環裏的話,曾經訪問next的時候就應該可以訪問到now,但是事實上並沒有訪問到,所以說明next和now不在一個環裏,那麼這個點也就是沒有意義,就可以不必去找next了。

在DFS的時候一路給點打上時間戳,用SPFA的思想,因爲我們從一個固定的點出發,小環中的邊比大環中的邊少,那麼如果一個點同時存在於小環和大環中的話,從小環走過來的時候時間戳肯定比大環走過來小,那麼我們每次如果找到一個點,當前的時間戳大於已有的時間戳的話,就將這個點更新,重新設爲可選。

如果我們某一次訪問到一個點k,滿足visited[k]=1,那麼p就是一個完整的環,用p來更新答案,同時用上面SPFA的思想看能否更新k點,如果能更新就繼續從k點往下走。

但是這樣遇到了一個問題,衆所周知SPFA的複雜度是O(EM)的,E是不確定的常數,也就是說在上文的方法中,如果某些點不斷被更新,就很容易被卡掉,如下就是一個例子:

 

(用紅點表示n/2+1號點)可以很明顯看出,答案應該是1->2->紅點->.....->n-1->n,但如果我們刻意控制一下讀入方式,導致遍歷的時候從1先到了2再到3一直到n/2號點,在n/2->紅點的時候更新了一下紅點的時間戳,於是紅點變成可訪問,花了O(n/2)的時間把剩下的點遍歷了一遍,回來以後到n/2-1的時候又更新了一下紅點,又花了O(n/2)的時間,這樣一直下去,總共紅點被更新了n/2次,那麼這樣的實際複雜度已經達到了O(n^2/4)的時間,在n=200000的時候是遠遠超過了限制。

我們的目的就是,優化,再優化!

對於這個問題,一種比較好的方法是A*或者估價函數,但如果是在考場上的話,隨機化也不失爲一種高效而簡潔的方法。我們對於每個點,將連出去的邊的順序隨機打亂然後再依次訪問,對於上文的情況,如果一路下去一直訪問到n/2的情況的可能性只有,考慮極限情況就是nlogn的複雜度,也就是運氣很差的情況下,一直訪問到logn號點,直到此時纔開始更新,那麼這樣的概率是,也就是,n越大,超時的可能性反而越小,只有十萬分之一,是在可接受範圍內的。

但是如果RP爆炸,真的遇到這樣的情況怎麼辦呢?

再優化!

可以發現,每次找到紅點的時候,接下來的一段都是重複走的,一條鏈和一條邊實際上是一個效果,所以我們考慮將每個單鏈都縮成一條邊,如果再像上圖那種情況的話,紅點和1之間的鏈縮成一條邊,複雜度直接降了一個O(n),總複雜度最壞也只有O(n),至此,這題的複雜度仍然是O(EM),但是E已經降成了一個很小的常數了。

不過還不夠,我們還有更好的優化!

之前的visited[i]=2,說明i是一個不用訪問的點,但事實上,我們還是枚舉了i,對常數還是造成了一點影響,所以我們考慮可不可以讓枚舉的每個點都是有意義的點。

最終的結果是要找最小環,既然是個環,那我們就在環裏找環,不在環裏的點就直接拋棄掉。所以我們先做一遍tarjan,將所有的大環提取出來,然後再從每個環裏按上文方法進行遍歷,這樣的話找最小環的複雜度就趨於穩定了。

總複雜度:O(N+M+KM),常數K在最壞情況下爲10左右。

至此,這道題就基本解決了,是否還有更穩定更快的方法,仍然會繼續探究。

<del>隨便口胡,錯誤一大堆,歡迎打臉</del>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章