SPOJ : Weighted Sum - algorithm

You are given N integers, A[1] to A[N]. You have to assign weights to these integers such that their weighted sum is maximized. The weights should satisfy the following conditions :
Each weight should be an positive integer.
W[1] = 1
W[i] should be in the range [2, W[i-1] + 1] for i > 1
Weighted sum is defined as S = A[1] * W[1] + A[2] * W[2] + ... + A[N] * W[N]
eg :
n=4 , array[]={ 1 2 3 -4 } , answer = 6 when we assign { 1 2 3 2 } respective weights .
So, as far as my understanding and research , no Greed solution is possible for it . I worked out many testcases on pen n paper , but couldn't get a greedy strategy .
Any ideas/hints/approaches people .

Let dp[i][j] equal the maximum weighted sum we can make from A[1..i] by assigning weight j to A[i]. Clearly dp[i][j] = j*A[i] + max(dp[i - 1][(j - 1)..N]). There are O(N^2) states and our recurrence takes O(N) for each state so the overall time complexity will be O(N^3). To reduce it to O(N^2) we can notice that there is significant overlap in our recurrence.
If dp[i][j] = j * A[i] + max(dp[i - 1][(j - 1)..N]), then
dp[i][j - 1] = (j - 1)*A[i] + max(dp[i - 1][(j - 2)..N]) = (j - 1)*A[i] + max(dp[i - 1][j - 2], dp[i - 1][(j - 1)..N]) = (j - 1)*A[i] + max(dp[i - 1][j - 2], dp[i][j] - j*A[i])
Which means the recurrence takes only O(1) to compute, giving you O(N^2) time overall.

Fairly standard dynamic-programming methods can be used to solve this problem in O(N³) time. Let V(k,u) denote the best value that can be gotten using elements k...N when Wₖ₋₁ has the value u. Observe that V(k,u) is the maximum value of g·Aₖ+V(k-1,g) as g ranges from 2 to u+1, and that V(N,u) is (u+1)·AN if AN is positive, else 2·AN.
Note that u is at most k in any V(k,u) calculation, so there are N*(N-1)/2 possible values of (k,u), so the method as outlined uses O(N²) space and O(N³) time.

Here's a little insight that might enable you or someone else to come up with a really fast solution. Note that for an optimal solution, you can safely assume that at each step either you increase the weight by +1 from the previous weight, or you decrease the weight all the way down to the minimum of 2. To see this, suppose you have an optimal solution that violates the property. Then you have some weight > 2 at some position i-1 and the next weight is also > 2 at position i but not an increase. Now consider the maximal length weakly increasing sub-sequence of weights in the optimal solution starting at position i (weakly increasing means that at each step in the sub-sequence, the weight does not decrease). By assumption, the optimal solution with this sub-sequence is no worse than the same solution except with the sub-sequence having 1 subtracted from all its weights. But this means that increasing all the weights in the sub-sequence by 1 will also not make the optimal solution any worse. Thus for an optimal solution, at each step you can safely assume that either you increase the weight by 1 or you set the weight to the minimum of 2.

Related

Get highest score in this game: choosing and removing elements in an array

Given an array arr of n integers, what is the highest score that a player can reach, playing the following game?
Choose an index 0 < i < n-1 in the array
Add arr[i-1] * arr[i+1] points to the score (initially the score is 0)
Shrink the array by removing element i (forall j >= i: arr[j] = arr[j+1]; then n = n - 1
Repeat steps 1-3 until n == 2.
Do the above until there are only 2 elements (which are the first and the last element because you can't remove them).
What is the highest score you can get ?
Example
arr = [1 2 3 4]
Choose i=2, get: 2*4 = 8 points, remove 3
Remaining: arr = [1 2 4]
Choose i=1, get 1*4 = 4 points, remove 2
Remaining: arr = [1 4].
The sum of points is 8 + 4 = 12, which is the highest possible score on this example.
I think it is related to Dynamic programming but I'm not sure how to solve it.
This problem has a dynamic programming approach similar to Matrix-chain multiplication problem. You can find further explanation in the book "Introduction to Algorithms", 3rd Edition (Cormen, page 370).
Let's find the optimal substructure property and then use it to construct an optimal solution to the problem from optimal solutions to subproblems.
Notation: Ci..j, where i ≤ j, stands for elements Ci,Ci+1,...,Cj.
Definition: A removal sequence for Ci..j is a permutation of i+1,i+2,...,j-1.
A removal sequence for Ci..j is optimal if the score achieved by removing the elements of Ci..j in that order is maximum among all possible removal sequences for Ci..j.
1. Characterize the structure of an optimal solution
If the problem is nontrivial, i.e. i + 1 < j, then any solution has a last removed element which corresponding index is k in the range
i < k < j. Such k split the problem into Ci..k and Ck..j. That is, for some value k, we first remove non extremal elements of Ci..k and Ck..j and then we remove element k. As removing non extremal elements of Ci..k doesn't affect score obtained by removing non extremal elements of Ck..j and an analogous reasoning for removing non extremal elements of Ck..j is also true we state that both subproblems are independent. Then, for a given removal sequence where kth-element is last, the score of Ci..j is equal to the sum of scores of Ci..k and Ck..j, plus the score of removing kth-element (C[i] * C[j]).
The optimal substructure of this problem is as follows. Suppose there is an optimal removal sequence O for Ci..j that ends at kth-element, then the ordering of removed elements from Ci..k must be optimal too. We can prove it by contradiction: If there was a removal sequence for Ci..k that scored higher than removal subsequence extracted from O for Ci..k then we can produce another removal sequence for Ci..j with higher score than optimal removal sequence (contradiction). A similar observation holds for the ordering of removed elements from Ck..j in the optimal removal sequence for Ci..j: it must be optimal too.
We can build an optimal solution for nontrivial instances of the problem by splitting the problem into two subproblems, finding optimal solutions to subproblem instances, and them combining these optimal subproblem solutions.
2. Recursively define the value of an optimal solution.
For this problem our subproblems are the maximum score obtained in Ci..j for 1 ≤ i ≤ j ≤ N. Let S[i, j] be the maximum score obtained in Ci..j; for the full problem, the highest score when evaluating the given rules is S[1, N].
We can define S[i, j] recursively as follows:
If j ≤ i + 1 then S[i, j] = 0
If i + 1 < j then S[i, j] = maxi < k < j{S[i, k] + S[k, j] + C[i] * C[j]}
We ensure that we search for the correct place to split because we consider all possible places, so that we are sure of having examined the optimal one.
3. Compute the value of an optimal solution
You can use your favorite method to compute S:
top-down approach (recursive)
bottom-up approach (iterative)\
I would use bottom-up for computing the solution since it would be < 5 lines long in almost any programming language.
Example in C++11:
for(int l = 2; l <= N; ++l) \\ increasing length intervals
for(int i = 1, j = i + l; j <= N; ++i, ++j)
for(int k = i + 1; k < j; ++k)
S[i, j] = max(S[i, j], S[i, k] + S[k, j] + C[i] * C[j])
4. Time Complexity and Space Complexity
There are nC2 + n = Θ(n2) subproblems and every subproblem do an operation which running time is Θ(l) where l is length of the subproblem so the math yield a running time of Θ(n3) for the algorithm (it's easy to spot the O(n3) part :-)). Also, the algorithm requires Θ(n2) space to store the S table.

How to solve Twisty Movement from Codeforces?

I've read the editorial but it's very short and claims something I don't understand why it's true. Why is it equivalent to finding longest subsequence of 1*2*1*2*?. Can someone explain the solution step by step and justify the claims at every step? http://codeforces.com/contest/934/problem/C
Here is the 'solution' from the editorial, but as I said it's very short and I don't understand it. Hope someone can guide me to the solution step by step justifying the claims along the way, not like in the solution here. Thanks.
Since 1 ≤ ai ≤ 2, it's equivalent to find a longest subsequence like
1 * 2 * 1 * 2 * . By an easy dynamic programming we can find it in
O(n) or O(n2) time. You can see O(n2) solution in the model
solution below. Here we introduce an O(n) approach: Since the
subsequence can be split into 4 parts (11...22...11...22...) , we
can set dp[i][j](i = 1...n, j = 0..3) be the longest subsequence of
a[1...i] with first j parts.
I also think that the cited explanation is not super clear. Here is another take.
You can collapse an original array
1 1 2 2 2 1 1 2 2 1
into a weighted array
2 3 2 2 1
^ ^ ^ ^ ^
1 2 1 2 1
where numbers at the top represent lengths of contiguous strips of repeated values in the original array.
We can convince ourselves that
The optimal flip does not "break up" any contiguous sequences.
The optimal flip starts and ends with different values (i.e. starts with 1 and ends with 2, or starts with 2 and ends with 1).
Hence, the weighted array contains enough information to solve the problem. We want to flip a contiguous slice of the weighted array s.t. the sum of weights associated with some contiguous monotonic sequence is maximized.
Specifically, we want to perform the flip in such a way that some contiguous monotonic sequence 112, 122, 211 or 221 has maximum weight.
One way to do this with dynamic programming is by creating 4 auxiliary arrays.
A[i] : maximal weight of any 1 to the right of i.
B[i] : maximal weight of any 1 to the left of i.
C[i] : maximal weight of any 2 to the right of i.
D[i] : maximal weight of any 2 to the left of i.
Let's assume that if any of A,B,C,D is accessed out of bounds, the returned value is 0.
We initialize x = 0 and do one pass through the array Arr = [1, 2, 1, 2, 1] with weights W = [2, 3, 2, 2, 1]. At each index i, we have 2 cases:
Arr[i:i+2] == 1 2. In this case we set
x = max(x, W[i] + W[i+1] + C[i+1], W[i] + W[i+1] + B[i-1]).
Arr[i:i+2] == 2 1. In this case we set
x = max(x, W[i] + W[i+1] + A[i+1], W[i] + W[i+1] + D[i-1]).
The resulting x is our answer. This is an O(N) solution.

Coin change(Dynamic programming)

I have a question about the coin change problem where we not only have to print the number of ways to change $n with the given coin denominations for eg {1,5,10,25}, but also print the ways
For example if the target = $50, and the coins are {1,5,10,25}, then the ways to actually get use the coins to get the target are
2 × $25
1 × $25 + 2 × $10 + 1 × $5
etc.
What is the best time complexity we could get to solve this problem?
I tried to modify the dynamic programming solution for the coin change problem where we only need the number of ways but not the actual ways
I am having trouble figuring out the time complexity.
I do use memorization so that I don't have to solve the same problem again for the given coin and sum value but still we need to iterate through all the solution and print them. So the time complexity is definitely more than O(ns) where n is the number of coins and s is the target
Is it exponential? Any help will be much appreciated
Printing Combinations
def coin_change_solutions(coins, S):
# create an S x N table for memoization
N = len(coins)
sols = [[[] for n in xrange(N + 1)] for s in xrange(S + 1)]
for n in range(0, N + 1):
sols[0][n].append([])
# fill table using bottom-up dynamic programming
for s in range(1, S+1):
for n in range(1, N+1):
without_last = sols[s][n - 1]
if (coins[n - 1] <= s):
with_last = [list(sol) + [coins[n-1]] for sol in sols[s - coins[n - 1]][n]]
else:
with_last = []
sols[s][n] = without_last + with_last
return sols[S][N]
print coin_change_solutions([1,2], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [2, 2]]
without: we don't need to use the last coin to make the sum. All the coin solutions are found directly by recursing to solution[s][n-1]. We take all those coin combinations and copy them to with_last_sols.
with: we do need to use the last coin. So that coin must be in our solution. The remaining coins are found recursively via sol[s - coins[n - 1]][n]. Reading this entry will give us many possible choices for what the remaining coins should be. For each possible choice , sol, we append the last coin, coin[n - 1]:
# For example, suppose target is s = 4
# We're finding solutions that use the last coin.
# Suppose the last coin has a value of 2:
#
# find possible combinations that add up to 4 - 2 = 2:
# ===> [[1,1], [2]]
# then for each combination, add the last coin
# so that the combination adds up to 4)
# ===> [[1,1,2], [2,2]]
The final list of combinations is found by taking the combinations for the first case and the second case and concatenating the two lists.
without_last_sols = [[1,1,1,1]]
with_last_sols = [[1,1,2], [2,2]]
without_last_sols + with_last_sols = [[1,1,1,1], [1,1,2], [2,2]]
Time Complexity
In the worst case we have a coin set with all coins from 1 to n: coins
= [1,2,3,4,...,n] – the number of possible coin sum combinations, num solutions, is equal to the number of integer partitions of s, p(s).
It can be shown that the number of integer partitions, p(s) grows exponentially.
Hence num solutions = p(s) = O(2^s). Any solution must have this at a minimum so that it can print out all these possible solutions. Hence the problem is exponential in nature.
We have two loops: one loop for s and the other loop for n.
For each s and n, we compute sols[s][n]:
without: We look at the O(2^s) combinations in sol[s - coins[n - 1]][n]. For each combination, we copy it in O(n) time. So overall this takes: O(n×2^s) time.
with: We look at all O(2^s) combinations in sol[s][n]. For each combination list sol, we create copy of that new list in O(n) time and then append the last coin. Overall this case takes O(n×2^s).
Hence the time complexity is O(s×n)×O(n2^s + n2^s) = O(s×n^2×2^s).
Space Complexity
The space complexity is O(s×n^2×2^s) because we have a s×n table with
each entry storing O(2^s) possible combinations, (e.g. [[1, 1, 1, 1], [1, 1, 2], [2, 2]]), with each combination, (e.g. [1,1,1,1]) taking O(n) space.
What I tend to do is solve the problem recursively and then build a memoization solution from there.
Starting with a recursive one the approach is simple, pick a coin subtract from target and dont pick a coin.
Whilst you pick a coin you add it to a vector or your list, when you dont pick one you pop the one you added before. The code looks something like:
void print(vector<int>& coinsUsed)
{
for(auto c : coinsUsed)
{
cout << c << ",";
}
cout << endl;
}
int helper(vector<int>& coins, int target, int index, vector<int>& coinsUsed)
{
if (index >= coins.size() || target < 0) return 0;
if (target == 0)
{
print(coinsUsed);
return 1;
}
coinsUsed.push_back(coins[index]);
int with = helper(coins, target - coins[index], index, coinsUsed);
coinsUsed.pop_back();
int without = helper(coins, target, index + 1, coinsUsed);
return with + without;
}
int coinChange(vector<int>& coins, int target)
{
vector<int> coinsUsed;
return helper(coins, target, 0, coinsUsed);
}
You can call it like:
vector<int> coins = {1,5,10,25};
cout << "Total Ways:" << coinChange(coins, 10);
So this gives you the total ways and also the coins used in the process to reach the target stored in coinsUsed you can now memoize this as you please by storing the passed in values in a cache.
The time complexity of the recursive solution is exponential.
link to the running program: http://coliru.stacked-crooked.com/a/5ef0ed76b7a496fe
Let d_i be a denomination, the value of a coin in cents. In your example d_i = {1, 5, 10, 25}.
Let k be the number of denominations (coins), here k = 4.
We will use a 2D array numberOfCoins[1..k][0..n] to determine the minimum number of coins required to make a change. The optimal solution is given by:
numberOfCoins[k][n] = min(numberOfCoins[i − 1][j], numberOfCoins[i][j − d_i] + 1)
The equation above represents the fact that to build an optimal solution we either do not use d_i, so we need use a smaller coin (this is why i is decremented below):
numberOfCoins[i][j] = numberOfCoins[i − 1][j] // eq1
or we use d_i, so we add +1 to the number of coins needed and we decrement by d_i (the value of the coin we just used):
numberOfCoins[i][j] = numberOfCoins[i][j − d_i] + 1 // eq2
The time complexity is O(kn) but in cases where k is small, as is the case in your example, we have O(4n) = O(n).
We will use another 2D array, coinUsed, having the same dimensions as numberOfCoins, to mark which coins were used. Each entry will either tell us that we did not use the coin in coinUsed[i][j] by setting a "^" in that position (this correspond to eq1). Or we mark that the coin was used by setting a "<" in that position (corresponding to eq2).
Both arrays can be built as the algorithm is working. We will only have constant more instructions in the inner loop, therefore the time complexity of building both arrays is still O(kn).
To print the solution we need to iterate, in the worse case scenario over k + n+1 elements. For example, when the optimal solution is using all 1 cent denominations. But note that printing is done after building so the overall time complexity is O(kn) + O(k + n+1). As before, if k is small the complexity is O(kn) + O(k + n+1) = O(kn) + O(n+1) = O(kn) + O(n) = O((k+1)n) = O(n).

Max coverage disjoint intervals

Assume you have k<=10^5 intervals [a_i, b_i] \in [1,10^18] (some of them may overlap), and you need to choose a set of intervals mutually disjoint such that their union is maximal. Not maximum number of disjoint intervals, but the union must cover the most.
Can't try all possible subsets 2^k infeasible.
Greedy approaches ordering by a_i ( interval covering algorithm) and ordering by b_i ( maximum number of disjoint intervals algorithm ) didn't work
Can't figure out if there is a dynamic program solution.
Given the size of the input, I think the solution should be O(k log k) or O(k)
Examples
1. [1,4], [3,5], [5,9], [7, 18]
Sol [3,5]u[7,18]
[1,2], [2,6], [3,4], [5,7]
Sol [1,2]u[3,4]u[5,7]
[2,30], [25,39], [30,40]
Sol [2,30]
The problem can be solved in O(k log(k)).
First sort the intervals by their upper bounds (the b_is). Let I(1), I(2), ..., I(k) be the list of sorted intervals. That is,
b_1 <= b_2 <= ... <= b_k
Denote by w(i) the length of interval I(i). That is,
w(i) = b_i - a_i
Denote by f(i) the total length of the optimal solution among those whose last interval is I(i). That is, the solution corresponding to f(i) is a set which:
contains the interval I(i)
doesn't contain any interval whose upper bound is above b_i
has the maximum cover among the sets of (non-overlapping) intervals satisfying 1+2
Now we are going to compute f(1), f(2), ..., f(k) and return the maximum value of them all. Clearly, the optimal solution corresponds to one of the f(i)s and therefore the maximal f(i) is the optimal solution.
To compute each f(i) we use dynamic programming. We do this by relying on the following recurrence relation:
f(i) = w(i) + max{f(j) | b_j < a_i}
I'll demonstrate the computation with your first input example:
I(1)=[1, 4], w(1)=3
I(2)=[3, 5], w(2)=2
I(3)=[5, 9], w(3)=4
I(4)=[7, 18], w(4)=11
We compute f(i) for i=1, 2, 3, 4:
f(1) = w(1) + max{None} = 3
f(1) intervals: {I(1)}
f(2) = w(2) + max{None} = 2
f(2) intervals: {I(2)}
f(3) = w(3) + max{f(1)} = 4 + 1 = 5
f(3) intervals = {I(1), I(3)}
f(4) = w(4) + max{f(1), f(2)} = 11 + f(1) = 11 + 3 = 14
f(4) intervals = {I(1), I(4)}
The maximum f(i) is f(4) which corresponds to the set of intervals {I(1), I(4)}, the optimal solution.
There seems to be a O(k * log(k)) solution. It can be achieved with segment tree data structure.
We may at first populate some endPos array of segment endings, sort it. Memorise for each of the segments corresponding endPos index. For this let endPosIdx be such array that endPosIdxj will store an index in endPos where the j-th segment ends.
Next we will introduce a segment tree. It will process the following requests:
1. getMax(i) - get maximum value on the range [0, i].
2. update(i, value) - update maximum at i-th position with value.
i is and index in endPos array. Calling getMax(i) we ask for what maximum cover can we achieve if non of the segments ends after endPosi. Calling update(i, value) we say that now there exists a cover with length value ending at endPosi.
Sort all segments in increasing order by their starting position aj. Process them in that order. The gist is to find the largest cover if we will certainly take current segment in resulting set. Current cover will equal to the sum of the length of current segment and max cover of the segments ending before current. Let j be the index of current segment (they are sorted by start pos). Let i then be such max index that endPosi ≤ aj (i may be found from j by binary search). Then we can find
coverj = lengthj + getMax(i)
Next we should update segment tree calling update(endPosIdxj, coverj) and proceed to the next segment.
After processing of all the segments the solution can be found by calling getMax(size(endPos)).

Find largest continuous sum such that the minimum of it and it's complement is largest

I'm given a sequence of numbers a_1,a_2,...,a_n. It's sum is S=a_1+a_2+...+a_n and I need to find a subsequence a_i,...,a_j such that min(S-(a_i+...+a_j),a_i+...+a_j) is the largest possible (both sums must be non-empty).
Example:
1,2,3,4,5 the sequence is 3,4, because then min(S-(a_i+...+a_j),a_i+...+a_j)=min(8,7)=7 (and it's the largest possible which can be checked for other subsequences).
I tried to do this the hard way.
I load all values into the array tab[n].
I do this n-1 times tab[i]+=tab[i-j]. So that tab[j] is the sum from the beginning till j.
I check all possible sums a_i+...+a_j=tab[j]-tab[i-1] and substract it from the sum, take the minimum and see if it's larger than before.
It takes O(n^2). This makes me very sad and miserable. Is there a better way?
Seems like this can be done in O(n) time.
Compute the sum S. The ideal subsequence sum is the longest one which gets closest to S/2.
Start with i=j=0 and increase j until sum(a_i..a_j) and sum(a_i..a_{j+1}) are as close as possible to S/2. Note which ever is closer and save the values of i_best,j_best,sum_best.
Increment i and then increase j again until sum(a_i..a_j) and sum(a_i..a_{j+1}) are as close as possible to S/2. Note which ever is closer and replace the values of i_best,j_best,sum_best if they are better. Repeat this step until done.
Note that both i and j are never decremented, so they are changed a total of at most O(n) times. Since all other operations take only constant time, this results in an O(n) runtime for the entire algorithm.
Let's first do some clarifications.
A subsequence of a sequence is actually a subset of the indices of the sequence. Haivng said that, and specifically int he case where you sequence has distinct elements, your problem will reduce to the famous Partition problem, which is known to be NP-complete. If that is the case, you can manage to solve the problem in O(Sn) where "n" is the number of elements and "S" is the total sum. This is not polynomial time as "S" can be arbitrarily large.
So lets consider the case with a contiguous subsequence. You need to observe array elements twice. First run sums them up into some "S". In the second run you carefully adjust array length. Lets assume you know that a[i] + a[i + 1] + ... + a[j] > S / 2. Then you let i = i + 1 to reduce the sum. Conversely, if it was smaller, you would increase j.
This code runs in O(n).
Python code:
from math import fabs
a = [1, 2, 3, 4, 5]
i = 0
j = 0
S = sum(a)
s = 0
while s + a[j] <= S / 2:
s = s + a[j]
j = j + 1
s = s + a[j]
best_case = (i, j)
best_difference = fabs(S / 2 - s)
while True:
if fabs(S / 2 - s) < best_difference:
best_case = (i, j)
best_difference = fabs(S / 2 - s)
if s > S / 2:
s -= a[i]
i += 1
else:
j += 1
if j == len(a):
break
s += a[j]
print best_case
i = best_case[0]
j = best_case[1]
print "Best subarray = ", a[i:j + 1]
print "Best sum = " , sum(a[i:j + 1])

Resources