趣題:怎麼從一幫女裝大佬中分辨出誰是真妹子?(大霧)

嘗試一篇仿m67風格的博客。

你受邀參加一場女裝聚會,除了你和聚會組織者之外還有n個人參加,這些人你都不認識。

組織者告訴你:這場聚會上所有人都會穿女裝,但其中有一部分是真妹子,另外一些則是女裝大佬。

作爲直男,你的目的顯然是去欣賞妹子而不是女裝大佬。然而到現場之後你慌了——單身多年的你竟然分辨不出哪些人是妹子。泡到一個女裝大佬顯然是一個讓人想想就菊花一緊的事,你又不想白來一趟,於是趕緊向組織者求助。

你以爲你會很快得到想要的答案?哈哈你想得太簡單了,這種魔術揭祕一般的事怎麼可能隨便告訴你,不然聚會的樂趣全無。不過,看着你飢渴可憐的眼神,組織者還是決定幫你一把:

不妨把所有人按照1~n編號。每次你可以向組織者給出兩個編號集合,每個集合裏元素不能重複(但兩個集合可以有相同元素)。組織者會告訴你:其中某個集合裏的妹子數量,一定不少於另一個集合。

舉個例子:你向組織者詢問集合{1,2,3,6,8}和{2,3,4,5},組織者回答第二個集合,就代表集合{2,3,4,5}中的妹子人數一定不少於集合{1,2,3,6,8}中的妹子人數。

需要注意的是,如果兩個集合妹子人數一樣,則組織者無論回答哪個集合都是合理的,此時他會任選一個集合回答你。

組織者信心滿滿地認爲,即使他給出了這些信息,你還是無法確定出所有人的性別。他是對的,舉個最簡單的例子,當n=1時,你顯然無法得到任何有用的信息。於是,他又向你透露了兩個關鍵信息:

1、這場聚會中至少有一個妹子,也就是說絕對不會所有人都是女裝大佬。不過不排除所有人都是妹子而沒有女裝大佬的情況。

2、他告訴你了場上妹子人數的奇偶性。

組織者認爲即使這樣你還是沒轍,誰知你卻已經有了一個方案,能問出所有人的性別!不僅如此,你還打算用最省事的方式問出答案。這裏的“最省事”不是詢問次數最少,而是詢問的總人次數(也可以看做所有詢問的集合大小總和)最小。

試問:你有什麼好的方案?注意你給出的方案應該能在任何情況下(比如,你可以假定在所有兩集合相等時都回答對你最不利的答案)都能給出正確答案。

 

 

 

 

 

 

 

 

 

 

這題第一眼看上去比較簡單,不過在多次嘗試之後,想必你已經發現了:“兩集合相同時返回任意一個”這個設定遠比你想象中的坑得多。比如,你永遠無法直接詢問兩個集合來得到它們之間準確的大小關係——即使你詢問了許多次得到的結果都相同,你又怎麼能確定,確實是這個集合更大,而不是兩個集合相等而你運氣背到了一定程度呢?(別忘了,你需要考慮的是對你最不利的情況。)

在接下來的敘述中,我們不妨把問題抽象成如下模型:我們要確定n個01變量的值,其中某個變量爲0代表女裝大佬,1代表妹子,並定義集合之間的“大於等於”運算指的是集合中1的個數之間的比較,定義詢問總代價爲所有詢問的集合大小總和。

首先一個自然的想法是,如果把所有人兩兩比較一次會怎樣?每個人都會進行n-1次比較,並且可以發現一個關鍵的性質:對於任意一個值爲0的變量x和任意一個值爲1的變量y來說,x大於等於其他元素的次數一定比y大於等於其他元素的次數多。

這是顯然的,不妨設所有變量中有a個的值爲0,則任意一個0最多隻能大於等於a-1的元素,而任意一個1至少能大於等於a個元素。

因此,如果把所有變量按照大於等於其他變量的次數進行從大到小排序,則一定存在一個分界點,它的左側全都是1,右側全都是0。

問題是,怎麼確定這個分界點在哪裏呢?別忘了,保證至少存在一個變量爲1,所以排在最靠前的元素一定是1。接下來,我們就可以枚舉每一對相鄰的元素,把它們放在一個集合(記做S),把這個確定的1放在另一個集合(記做T)。

接下來,有3種可能的情況發生:

1、這兩個相鄰的元素都是1,則一定會回答T。

2、這兩個相鄰的元素都是0,則一定會回答S。

3、這兩個相鄰的元素是一個1一個0,則返回S或T都有可能。

換句話說,對於兩種可能的回答,我們能得到的信息如下:

1、如果回答的是T,說明兩個元素是2個1,或者一個1一個0。

2、如果回答的是S,說明兩個元素是2個0,或者一個1一個0。

由於我們已經知道了當前的序列一定是前面一段1後面一段0,所以詢問結果也一定是前面一段T後面一段S。問題是,中間分界處有一處0和1相鄰的情況,回答序列也有一處S和T相鄰的情況,如何知道此處具體哪個回答對應的是一個0一個1的情況?

奇偶性!別忘了我們還有這麼一個條件。因爲中間的不確定性因素最多隻有1,再結合1的個數的奇偶性我們就能得到一個準確的答案。

此時我們終於得到了一個確定性的解答,它的詢問總代價是O(n^2)級別的。還能做得更好嗎?

細心的讀者可能已經發現,最後一步枚舉所有的相鄰對其實是沒必要的。因爲我們已經知道詢問的結果必然是一段T一段S,我們就可以通過二分的方法確定分界點的位置在哪裏。不過,這裏的優化只是把一個O(n)變成了O(logn),我們還是需要O(n^2)級別的總代價。

但是……再仔細想想,我們爲什麼會得到一個“詢問的結果必然是一段T一段S”的結果?本質原因是我們把所有元素排成了前面一段1後面一段0……排序!我們第1步花O(n^2)的代價,本質上只是將這n個元素排了個序而已。不過我們既然可以直接對兩個元素進行比較,爲什麼不直接進行排序呢?

衆所周知,不少基於比較的排序算法的比較次數都是O(nlogn)級別,利用我們的“大於等於”運算,當然可以直接實現一個基於比較的排序。這樣,我們就把總代價降低到了O(nlogn)級別。

還能更優一點嗎?注意到基於比較的排序複雜度下界也是O(nlogn)級別,因此這種方法也就走到頭了。

當時我做這個題的時候,認爲O(nlogn)級別的代價就是最優方案了。事後才得知,居然有更優秀的方案,可以把總代價降低到O(n)級別!究其原因,還是因爲我們的權值只有01兩種,正常排序的話使用計數排序顯然是O(n)的,我們卻需要基於比較的O(nlogn)排序,這顯然是一種浪費。

 

 

 

 

 

 

 

 

 

 

首先考慮我們能否找到一個值爲1的元素出來。這本質上就是在找最大值,利用大於等於運算,我們能輕鬆地在O(n)的代價之內找出來。設這個元素爲q。

然後呢?我們就可以利用這一個確定爲1的元素,逐步確定其餘所有的元素。

首先,如果當前僅剩1個未確定的元素,我們容易根據奇偶性確定出它的值。

否則,任取兩個未確定的元素x,y,首先對它們進行一次比較,不妨設x>=y。

然後,再令S={x,y},T={q},對S和T進行一次比較。

如果S>=T,意味着x和y中至少有1個爲1。

如果T>=S,意味着x和y中至少有1個爲0。

看起來我們還是不能確定每個元素的值,然而這個算法的最精妙之處就在這裏:

以S>=T爲例,此時x和y中至少有1個爲1。然而我們又有x>=y,這意味着,如果x爲0,那麼y也必須爲0,這就會產生矛盾。於是,此時x必須爲1——儘管我們仍然不知道y的值,但是x的值我們可以確定了!

同樣,如果S<=T,也可以推出y必須爲0。

這個思路不可謂不令人驚訝——通過如上的操作,無論是x還是y我們都不能保證一定能求出它的準確值,但我們一定能求出其中某一個的值!

同時注意到進行一次上述操作的代價爲5,因此求出所有元素的值,總代價是O(n)的。

這麼長的文章看到這裏也不容易,相信你如果還沒被繞暈的話也一定會讚歎這算法的精妙。此時我說,這種算法還不是最優,你也多半不會相信吧?畢竟我們已經把總代價降到了O(n),而顯然這也是下界了。

但是,真的有比這還優秀的方案!之前我們的算法雖然是O(n),但它還有常數項優化的空間。準確地說,由於第一步找最大值的代價約爲2n,第二步每確定一個元素的代價爲5,因此總代價約爲7n。接下來,我們將進一步突破這個下限。不過由於接下來的算法將會較爲複雜,建議先完全理解上述算法之後再繼續看下去。

 

 

 

 

 

 

 

 

 

我們嘗試把第一步那個找最大值的過程去掉,隨便取一個元素作爲基準值。

那如果這個元素爲0怎麼辦?不慌,我們冷靜分析一下。

取元素a,b,c,假設b>=c,構造集合S={b,c},T={a}。

如果S<=T,則不管a的值如何,a與b中均不可能都是1。此時,我們依然能確定出c=0。下一次操作時,保持當前的a不變,另取一個新的元素,與原來的b作爲新的b和c即可。

如果S>=T呢?如果a是1,我們就已經能知道b一定是1了,然而現在a的值不確定,因此我們還不能給出“b一定是1”的判斷。

然而,“如果a是1,我們就已經能知道b一定是1了”這話意味着什麼?意味着b>=a!也就是說,我們沒有直接比較a與b,就憑空得出了這一組大小關係。我們把這組關係記錄下來,緊接着用b去替換a,再取一個新的元素,與原來的c作爲新的b和c。

反覆操作直到所有元素都使用了,此時我們會得到什麼?若干個已經確定爲0的元素,一連串元素x1x2...xk滿足x1<=x2<=...<=xk,還有一個額外的元素是最後一次操作剩下的,記做p。

容易發現,在xk和p之間一定存在一個元素的值爲1,因此我們可以用一次比較確定一個1。

那麼其餘的未確定元素呢?我們發現它們已經天然地排成了有序的,除了最多一個可能的元素遊離於這條鏈之外(即元素p,如果p<=xk的話)。我們就可以利用之前提到的二分算法在鏈上找到一個分界點,對於分界點處的元素和p,我們也可以用至多1次額外比較和奇偶性確定出來。

因此我們終於得到了這題的最優解:總代價5n+O(logn)的優秀算法。(你還有其它更優的解嗎?歡迎指出。)

唉,如果現實中真有這種聚會該多好……

 

 

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