演算法的優雅(三)卡牌的秘密

2021-06-09 20:26:10 字數 1393 閱讀 8326

現在大學男生宿舍和3年前有什麼不一樣?答案是:以前他們都玩dota,而現在很多人在玩lol。

作為乙個職業宅男,lol也成為筆者的一部分了,而筆者最喜歡的英雄除了暴力小蘿莉安妮外,還有乙個就是卡牌大師——一切盡在卡牌中。

問題來了:一副撲克牌除去大小王,在剩下52張牌中,隨意抽出一張牌,用盡可能快盡可能少的空間求出這張牌的點數(不要求花色)。

解法一:

對於學了多年程式設計的讀者,不難想出乙個方法,用乙個陣列,初始化為0,之後53張牌從頭到尾掃一遍,每次點數的陣列位置+1,最後掃一遍陣列,3次的就是被抽出牌的點數了。

這樣,我們得到了答案,時間複雜度是o(n),空間複雜度是o(k),k是數的最大值。

那麼,我們能不能讓複雜度降低呢?

解法二:

首先,我們不難證出,求出抽出牌點數的演算法至少要將牌組掃一遍,所以時間複雜度o(n)不可能降低了,那麼讓我們看看空間複雜度。

還記的資料結構課學的一種查詢方法麼?沒錯,即使雜湊表。

建立雜湊表,之後每次存入資料,如果同一位置出現兩次,那麼這個位置肯定不是抽出的排了,所以該位置可以釋放給別的資料用了。

所以,在空間上,最好情況是o(1),最壞情況依舊是o(k)。

解法三:

有沒有比較穩定的空間複雜度呢?

讓我們想想我們還學過什麼。

在c++,演算法、資料結構、離散數學、組成原理、數電、微型計算機等課程中,我們都學過乙個東西,但是老師們都沒有強調過,那就是位運算。

位運算是一種很神奇的運算方式,要比普通的四則運算快很多,而在位運算中有個xor/抑或。

a xor a = 0, a xor 0 = a,而且滿足結合律和交換律;

而正常牌出現是偶數次,而只有被抽出的牌是奇數次,所以,將所有數xor一遍,結果就出來,而且空間複雜度是o(1)

解法四:

讓我們看看有沒有別方法。

首先,這是一副牌,我們知道什麼資訊呢?

所有52張牌的點數我們是知道的,那麼它們的和也就知道了,如果用總和減去53張牌的點數,結果就是抽出的牌咯。

追問環節:

如果我抽出兩張牌怎麼辦呢?

顯然前三種方法可以解決,但xor方法明顯出現的侷限性,讓我們看看第四種方法怎麼辦。

設這兩張牌是x和y,那麼根據第四種方法我們能得到x+y,顯然這個解釋不確定的,而解乙個二元方程,我們還需要另乙個方程。

那麼,我們知道總和確定,那麼總乘積也知道的,於是xy的值也就知道了。(如果乘積很大,我們可以用平方和來做。)

於是這個二元方程組就聯立好了。

繼續追問環節:

如果是抽出了k張牌怎麼辦。

這道題沒有標準答案,雖然我們可以用第四種方法求解方程組(貌似有一種特殊的行列式是第一行是0次冪,第二行1次冪,第三行2次冪....我忘了叫什麼了....),顯然這個方法不是很好,這時候hash是最好的選擇了,即容易實現,而且能節省空間。

莫隊 優雅的暴力演算法

例題 牛客暑期訓練營 j 題意描述 輸入一串整數 n 1e5 對於每個詢問 i,j 輸出a 1 a i 以及a j a n 裡數字的種類數,詢問一共q個 n 思路分析 暴力解法,即對於每個詢問均通過遍歷尋找答案,會令複雜度高達o n 2 考慮對其進行優化。我們知道對於詢問 i,j 去掉i對結果造成的...

優雅的python 寫排序演算法

arr while true 輸入資料 當輸入q結束 a raw input if a q break s len arr for i in range s 氣泡排序 for j in range s i 1 if arr j arr j 1 arr j arr j 1 arr j 1 arr j ...

關於撲克牌的演算法

紅色標記的地方是我標註的乙個重點 實現乙個演算法 將一副54張撲克牌經過洗牌後順序發給三個人,然後將每個人的牌按降序排序。我們已經設計出了撲克牌類card 演算法類game,並且有了測試 main.cpp,但演算法類game中的洗牌函式shuffle 和發牌函式deal 沒有實現,請寫出具體的實現 ...