Find the maximum number of points per game - algorithm

The input is an array of cards. In one move, you can remove any group of consecutive identical cards. For removing k cards, you get k * k points. Find the maximum number of points you can get per game.
Time limit: O(n4)
Example:
Input: [1, 8, 7, 7, 7, 8, 4, 8, 1]
Output: 23
Does anyone have an idea how to solve this?
To clarify, in the given example, one path to the best solution is
Remove Points Total new hand
3 7s 9 9 [1, 8, 8, 4, 8, 1]
1 4 1 10 [1, 8, 8, 8, 1]
3 8s 9 19 [1, 1]
2 1s 4 23 []

Approach
Recursion would fit well here.
First, identify the contiguous sequences in the array -- one lemma of this problem is that if you decide to remove at least one 7, you want to remove the entire sequence of three. From here on, you'll work with both cards and quantities. For instance,
card = [1, 8, 7, 8, 4, 8, 1]
quant = [1, 1, 3, 1, 1, 1, 1]
Now you're ready for the actual solving. Iterate through the array. For each element, remove that element, and add the score for that move.
Check to see whether the elements on either side match; if so, merge those entries. Recur on the remaining array.
For instance, here's the first turn of what will prove to be the optimal solution for the given input:
Choose and remove the three 7's
card = [1, 8, 8, 4, 8, 1]
quant = [1, 1, 1, 1, 1, 1]
score = score + 3*3
Merge the adjacent 8 entries:
card = [1, 8, 4, 8, 1]
quant = [1, 2, 1, 1, 1]
Recur on this game.
Improvement
Use dynamic programming: memoize the solution for every sub game.
Any card that appears only once in the card array can be removed first, without loss of generality. In the given example, you can remove the 7's and the single 4 to improve the remaining search tree.

Related

How to optimize this for loop faster than O(N^3)?

My for loop prints all the consecutive subsequence of a list. For example, suppose a list contains [0, 1,2,3,4,5,6,7,8,9]. It prints,
0
0,1
0,1,2
0,1,2,3
........
0,1,2,3,4,5,6,7,8,9
1
1,2
1,2,3
1,2,3,4,5,6,7,8,9
........
8
8,9
9
for i in range(10)
for j in range(i, 10):
subseq = []
for k in range(i, j+1):
subseq.append(k)
print(subseq)
The current algorithmic complexity of this for loop is O(N^3). Is there any way to make this algorithm any faster?
I don't know Python (this is Python, right?), but something like this will be a little faster version of O(N^3) (see comments below):
for i in range(10):
subseq = []
for j in range(i, 10):
subseq.append(j)
print(subseq)
Yes, that works:
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1]
[1, 2]
...
[7, 8]
[7, 8, 9]
[8]
[8, 9]
[9]
It’s not possible to do this in less than O(n3) time because you’re printing a total of O(n3) items. Specifically, split the array in quarters and look at the middle two quarters of the array. Pick any element there - say, the one at position k. That will be printed in at least n2 / 4 different subarrays: pick any element in the first quarter, any element in the last quarter, and the subarray between those elements will contain the element at position k.
This means that any of the n / 2 items in the middle two quarters gets printed at least n2 / 4 times, so you print at least n3 / 8 total values. There’s no way to do that in better than O(n3) time.

kth largest element in range interval

Given a list of overlapping intervals of integers. I need to find the kth largest element.
Example:
List { (3,4), (2,8), (4,8), (1,3), (7,9) }
This interval represents numbers as
[3, 4], [2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8], [1, 2, 3], and [7, 8, 9].
If we merge and sort it in decreasing order, we get
9, 8, 8, 8, 7, 7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 1
Now the 4th largest number in the list is 8.
Can anyone please explain an efficient (we don't have to generate the list) algorithm to find the kth element given only a list of internals ?
Find out the largest number. You go through intervals and examine ends of intervals. In your case it is 9. Set k = 1, and L = 9.
Perhaps there are other 9s. Mark (7,9) interval as visited and check if any other intervals contains 9 a >= 9 && b <= '. In your case there is only one 9.
Decrement current largest number (L -= L) and clear history of visited intervals. And repeat checking intervals.
Every time you meet your current largest number within an interval you should increment k and mark the interval as visited. As soon as it becomes equal to kth the current greatest number L is your answer.

Computing number of sequences

I saw the following problem that I was unable to solve. What kind of algorithm will solve it?
We have been given a positive integer n. Let A be the set of all possible strings of length n where characters are from the set {1,2,3,4,5,6}, i.e. the results of dice thrown n times. How many elements of A contains at least one of the following strings as a substring:
1, 2, 3, 4, 5, 6
1, 1, 2, 2, 3, 3
4, 4, 5, 5, 6, 6
1, 1, 1, 2, 2, 2
3, 3, 3, 4, 4, 4
5, 5, 5, 6, 6, 6
1, 1, 1, 1, 1, 1
2, 2, 2, 2, 2, 2
3, 3, 3, 3, 3, 3
4, 4, 4, 4, 4, 4
5, 5, 5, 5, 5, 5
6, 6, 6, 6, 6, 6
I was wondering some kind of recursive approach but I got only mess when I tried to solve the problem.
I suggest reading up on the Aho-Corasick algorithm. This constructs a finite state machine based on a set of strings. (If your list of strings is fixed, you could even do this by hand.)
Once you have a finite state machine (with around 70 states), you should add an extra absorbing state to mark when any of the strings has been detected.
Now you problem is reduced to finding how many of the 6**n strings end up in the absorbing state after being pushed through the state machine.
You can do this by expressing the state machine as a matrix . Entry M[i,j] tells the number of ways of getting to state i from state j when one letter is added.
Finally you compute the matrix raised to the power n applied to an input vector that is all zeros except for a 1 in the position corresponding to the initial state. The number in the absorbing state position will tell you the total number of strings.
(You can use the standard matrix exponentiation algorithm to generate this answer in O(logn) time.)
What's wrong with your recursive approach, can you elaborate on that, anyway this can be solved using a recursive approach in O(6^n), but can be optimized using dp, using the fact that you only need to track the last 6 elements, so it can be done in O ( 6 * 2^6 * n) with dp.
rec (String cur, int step) {
if(step == n) return 0;
int ans = 0;
for(char c in { '1', '2', '3', '4', '5', '6' } {
if(cur.length < 6) cur += c
else {
shift(cur,1) // shift the string to the left by 1 step
cur[5] = c // add the new element to the end of the string
}
if(cur in list) ans += 1 + rec(cur, step+1) // list described in the question
else ans += rec(cur, step+1)
}
return ans;
}

Diff two sequences of identifier

Given two sequences of identifier, how to find the smallest operation sequence that will transform the first sequence of identifier to the second one.
Operation can be :
Insert an identifier at a given position
Remove the identifier from a given position
Move an identifier from a position to another
Note: identifiers are unique and can't appear twice in a sequence
Example:
Sequence1 [1, 2, 3, 4, 5]
Sequence2 [5, 1, 2, 9, 3, 7]
Result (index are 0 based) :
- Remove at 3
- Move from 3 to 0
- Insert '9' at 3
- Insert '7' at 5
Thanks !
Start by finding the longest common subsequence. This will identify the elements that will not move:
[(1), (2), (3), 4, 5]
Elements of the LCS are enclosed in parentheses.
Go through both sequences from index 0, recording the operations required to make the sequences identical. If the current item of the first sequence is not part of the LCS, remove it, and mark the place where it has been before, in case you need to insert it at a later time. If the current element is part of the LCS, insert the element from the second sequence in front of it. This could be either a simple insertion, or a move. If the item that you are inserting is in the original list, make it a move; otherwise, make it an insert.
Here is a demo using your example. Curly braces show the current element
[{(1)}, (2), (3), 4, 5] vs [{5}, 1, 2, 9, 3, 7]
1 is a member of LCS, so we must insert 5. 5 is in the original sequence, so we record a move: MOVE 4 to 0
[5, {(1)}, (2), (3), 4] vs [5, {1}, 2, 9, 3, 7]
Items are the same, so we move on to the next one:
[5, (1), {(2)}, (3), 4] vs [5, 1, {2}, 9, 3, 7]
Again the numbers are the same - move to the next one:
[5, (1), (2), {(3)}, 4] vs [5, 1, 2, {9}, 3, 7]
3 is a member of LCS, so we must insert 9. The original element does not have 9, so it's a simple insertion: INSERT 9 at 3
[5, (1), (2), 9, {(3)}, 4] vs [5, 1, 2, 9, {3}, 7]
Yet again the numbers are the same - move to the next one:
[5, (1), (2), 9, (3), {4}] vs [5, 1, 2, 9, 3, {7}]
'4' is not a member of LCS, so it gets deleted: DEL at 5
[5, (1), (2), 9, (3)] vs [5, 1, 2, 9, 3, {7}]
We reached the end of the first sequence - we simply add the remaining items of the second sequence to the first one, paying attention to the list of prior deletions. For example, if 7 had been removed earlier, we would transform that deletion into a move at this time. But since the original list did not have 7, we record our final operation: INS 7 at 5.
This metric is called Levenshtein distance or more precisely Damerau–Levenshtein distance.
There are implementations for almost every possible programming language, that you can use resolve the problem you described.

Is this equivalent to insertion sort?

Say we have a 0-indexed sequence S, take S[0] and insert it in a place in S where the next value is higher than S[0] and the previous value is lower than S[0]. Formally, S[i] should be placed in such a place where S[i-1] < S[i] < S[i+1]. Continue in order on the list doing the same with every item. Remove the element from the list before putting it in the correct place. After one iteration over the list the list should be ordered. I recently had an exam and I forgot insertion sort (don't laugh) and I did it like this. However, my professor marked it wrong. The algorithm, as far as I know, does produce a sorted list.
Works like this on a list:
Sorting [2, 8, 5, 4, 7, 0, 6, 1, 10, 3, 9]
[2, 8, 5, 4, 7, 0, 6, 1, 10, 3, 9]
[2, 8, 5, 4, 7, 0, 6, 1, 10, 3, 9]
[2, 5, 4, 7, 0, 6, 1, 8, 10, 3, 9]
[2, 4, 5, 7, 0, 6, 1, 8, 10, 3, 9]
[2, 4, 5, 7, 0, 6, 1, 8, 10, 3, 9]
[2, 4, 5, 0, 6, 1, 7, 8, 10, 3, 9]
[0, 2, 4, 5, 6, 1, 7, 8, 10, 3, 9]
[0, 2, 4, 5, 1, 6, 7, 8, 10, 3, 9]
[0, 1, 2, 4, 5, 6, 7, 8, 10, 3, 9]
[0, 1, 2, 4, 5, 6, 7, 8, 3, 9, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Since every time an element is inserted into the list up to (n-1) numbers in the list may be moved and we must do this n times the algorithm should run in O(n^2) time.
I had a Python implementation but I misplaced it somehow. I'll try to write it again in a bit, but it's kinda tricky to implement. Any ideas?
The Python implementation is here: http://dpaste.com/hold/522232/. It was written by busy_beaver from reddit.com when it was discussed here http://www.reddit.com/r/compsci/comments/ejaaz/is_this_equivalent_to_insertion_sort/
It's a while since this was asked, but none of the other answers contains a proof that this bizarre algorithm does in fact sort the list. So here goes.
Suppose that the original list is v1, v2, ..., vn. Then after i steps of the algorithm, I claim that the list looks like this:
w1,1, w1,2, ..., w1,r(1), vσ(1), w2,1, ... w2,r(2), vσ(2), w3,1 ... ... wi,r(i), vσ(i), ...
Where σ is the sorted permutation of v1 to vi and the w are elements vj with j > i. In other words, v1 to vi are found in sorted order, possibly interleaved with other elements. And moreover, wj,k ≤ vj for every j and k. So each of the correctly sorted elements is preceded by a (possibly empty) block of elements less than or equal to it.
Here's a run of the algorithm, with the sorted elements in bold, and the preceding blocks of elements in italics (where non-empty). You can see that each block of italicised elements is less than the bold element that follows it.
[4, 8, 6, 1, 2, 7, 5, 0, 3, 9]
[4, 8, 6, 1, 2, 7, 5, 0, 3, 9]
[4, 6, 1, 2, 7, 5, 0, 3, 8, 9]
[4, 1, 2, 6, 7, 5, 0, 3, 8, 9]
[1, 4, 2, 6, 7, 5, 0, 3, 8, 9]
[1, 2, 4, 6, 7, 5, 0, 3, 8, 9]
[1, 2, 4, 6, 5, 0, 3, 7, 8, 9]
[1, 2, 4, 5, 6, 0, 3, 7, 8, 9]
[0, 1, 2, 4, 5, 6, 3, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If my claim is true, then the algorithm sorts, because after n steps all the vi are in order, and there are no remaining elements to be interleaved. But is the claim really true?
Well, let's prove it by induction. It's certainly true when i = 0. Suppose it's true for i. Then when we run the (i + 1)st step, we pick vi+1 and move it into the first position where it fits. It certainly passes over all vj with j ≤ i and vj < vi+1 (since these are sorted by hypothesis, and each is preceded only by smaller-or-equal elements). It cannot pass over any vj with j ≤ i and vj ≥ vi+1, because there's some position in the block before vj where it will fit. So vi+1 ends up sorted with respect to all vj with j ≤ i. So it ends up somewhere in the block of elements before the next vj, and since it ends up in the first such position, the condition on the blocks is preserved. QED.
However, I don't blame your professor for marking it wrong. If you're going to invent an algorithm that no-one's seen before, it's up to you to prove it correct!
(The algorithm needs a name, so I propose fitsort, because we put each element in the first place where it fits.)
Your algorithm seems to me very different from insertion sort. In particular, it's very easy to prove that insertion sort works correctly (at each stage, the first however-many elements in the array are correctly sorted; proof by induction; done), whereas for your algorithm it seems much more difficult to prove this and it's not obvious exactly what partially-sorted-ness property it guarantees at any given point in its processing.
Similarly, it's very easy to prove that insertion sort always does at most n steps (where by a "step" I mean putting one element in the right place), whereas if I've understood your algorithm correctly it doesn't advance the which-element-to-process-next pointer if it's just moved an element to the right (or, to put it differently, it may sometimes have to process an element more than once) so it's not so clear that your algorithm really does take O(n^2) time in the worst case.
Insertion sort maintains the invariant that elements to the left of the current pointer are sorted. Progress is made by moving the element at the pointer to the left into its correct place and advancing the pointer.
Your algorithm does this, but sometimes it also does an additional step of moving the element at the pointer to the right without advancing the pointer. This makes the algorithm as a whole not an insertion sort, though you could call it a modified insertion sort due to the resemblance.
This algorithm runs in O(n²) on average like insertion sort (also like bubble sort). The best case for an insertion sort is O(n) on an already sorted list, for this algorithm it is O(n) but for a reverse-sorted list since you find the correct position for every element in a single comparison (but only if you leave the first, largest, element in place at the beginning when you can't find a good position for it).
A lot of professors are notorious for having the "that's not the answer I'm looking for" bug. Even if it's correct, they'll say it doesn't meet their criteria.
What you're doing seems like insertion sort, although using removes and inserts seems like it would only add unnecessary complexity.
What he might be saying is you're essentially "pulling out" the value and "dropping it back in" the correct spot. Your prof was probably looking for "swapping the value up (or down) until you found it's correct location."
They have the same result but they're different in implementation. Swapping would be faster, but not significantly so.
I have a hard time seeing that this is insert sort. Using insert sort, at each iteration, one more element would be placed correctly in the array. In your solution I do not see an element being "fully sorted" upon each iteration.
The insert sort algorithm begin:
let pos = 0
if pos == arraysize then return
find the smallest element in the remaining array from pos and swap it with the element at position pos
pos++
goto 2

Resources