LIS:wikipedia
There is one thing that I can\'t understand:
why is X[M[i]] a non-decreasing sequence?
You cannot understand, because the code in wikipedia is wrong(I strongly believe so). It is not only wrong but the variables are poorly named. But it allowed me to spend time to understand how it works :D.
Now after I read the patience-sort. I rewrote the algorithm. I also wrote the corrected binary search.
Like insertion sort, patience-sort finds appropriate place for the next item by doing binary search. The binary search is done on the card-piles built in sorted order. Let me assign a variable for the card-pile.(I am talking about playing cards because patience is a simplified card game).
//! card piles contain pile of cards, nth pile contains n cards.
int top_card_list[n+1];
for(int i = 0; i <= n; i++) {
top_card_list[i] = -1;
}
Now the top_card_list contains the top-card of the card pile of height n. Patience sort places the card in hand over the highest top-card that is smaller than it(or the opposite). For further sorting note, please refer to wikipedia page for patience sort.
3
* 7 2
-------------------------------------------------------------
Pile of cards above (top card is larger than lower cards)
(note that pile of card represents longest increasing subsequence too !)
Now to find a number while we do dynamic programming for longest-increasing subsequence, we run an inner loop which is O(n).
for(int i = 1; i < n; i++) { // outer loop
for(int j = 0; j < i; j++) { // inner loop
if(arr[i] > arr[j]) {
if(memo_len[i] < (memo_len[j]+1)) {
// relaxation
memo_len[i] = memo_len[j]+1;
result = std::max(result,memo_len[i]);
pred[i] = j;
}
}
}
}
And the inner-loop is there to find the highest-top card that is smaller than our card in hand.
But we know that we can do it by binary search ! (exercise: prove the correctness) In that way we can do that in O(log (number of piles)) time. Now O(number of piles) = O(number of cards)(but number of card is 52, it should be O(1)!, just joking!). So the total application runs in O(n log n) time.
Here is the revised the DP with binary search.
for(int i = 1; i < n; i++) {
pile_height[i] = 1;
const int j = pile_search(top_card_list, arr, pile_len, arr[i]);
if(arr[i] > arr[j]) {
if(pile_height[i] < (pile_height[j]+1)) {
// relaxation
pile_height[i] = pile_height[j]+1;
result = std::max(result,pile_height[i]);
pile_len = std::max(pile_len,pile_height[i]);
}
}
if(-1 == top_card_list[pile_height[i]] || arr[top_card_list[pile_height[i]]] > arr[i]) {
top_card_list[pile_height[i]] = i; // top card on the pile is now i
}
}
Here is the correct pile search below. It is simply a binary search, but it finds the index of the top-card which is smaller than card in hand.
inline static int pile_search(const int*top_card_list, const vector& arr, int pile_len, int strict_upper_limit) {
int start = 1,bound=pile_len;
while(start < bound) {
if(arr[top_card_list[bound]] < strict_upper_limit) {
return top_card_list[bound];
}
int mid = (start+bound)/2 + ((start+bound)&1);
if(arr[top_card_list[mid]] >= strict_upper_limit) {
// go lower
bound = mid-1;
} else {
start = mid;
}
}
return top_card_list[bound];
}
Notice that unlike wikipedia, it returns top_card_list[bound] (my fix). Also notice where the top_card_list[] is updated in the dp. This code is tested for the boundary cases. I hope it helps.