What is maximum water colledted between two histograms? - algorithm

I recently came across this problem:
You are given height of n histograms each of width 1. You have to choose any two histograms such that if it starts raining and all other histograms(except the two you have selected) are removed, then the water collected between the two histograms is maximised.
Input:
9
3 2 5 9 7 8 1 4 6
Output:
25
Between third and last histogram.
This is a variant of Trapping rain water problem.
I tried two solutions but both had worst case complexity of N^2. How can we optimise further.
Sol1: Brute force for every pair.
int maxWaterCollected(vector<int> hist, int n) {
int ans = 0;
for (int i= 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
ans = max(ans, min(hist[i], hist[j]) * (j - i - 1));
}
}
return ans;
}
Sol2: Keep a sequence of histograms in increasing order of height. For every histogram, find its best histogram in this sequence. now, if all histograms are in increasing order then this solution also becomes N^2.
int maxWaterCollected(vector<int> hist, int n) {
vector< pair<int, int> > increasingSeq(1, make_pair(hist[0], 0)); // initialised with 1st element.
int ans = 0;
for (int i = 1; i < n; i++) {
// compute best result from current increasing sequence
for (int j = 0; j < increasingSeq.size(); j++) {
ans = max(ans, min(hist[i], increasingSeq[j].first) * (i - increasingSeq[j].second - 1));
}
// add this histogram to sequence
if (hist[i] > increasingSeq.back().first) {
increasingSeq.push_back(make_pair(hist[i], i));
}
}
return ans;
}

Use 2 iterators, one from begin() and one from end() - 1.
until the 2 iterator are equal:
Compare current result with the max, and keep the max
Move the iterator with smaller value (begin -> end or end -> begin)
Complexity: O(n).

Jarod42 has the right idea, but it's unclear from his terse post why his algorithm, described below in Python, is correct:
def candidates(hist):
l = 0
r = len(hist) - 1
while l < r:
yield (r - l - 1) * min(hist[l], hist[r])
if hist[l] <= hist[r]:
l += 1
else:
r -= 1
def maxwater(hist):
return max(candidates(hist))
The proof of correctness is by induction: the optimal solution either (1) belongs to the candidates yielded so far or (2) chooses histograms inside [l, r]. The base case is simple, because all histograms are inside [0, len(hist) - 1].
Inductively, suppose that we're about to advance either l or r. These cases are symmetric, so let's assume that we're about to advance l. We know that hist[l] <= hist[r], so the value is (r - l - 1) * hist[l]. Given any other right endpoint r1 < r, the value is (r1 - l - 1) * min(hist[l], hist[r1]), which is less because r - l - 1 > r1 - l - 1 and hist[l] >= min(hist[l], hist[r1]). We can rule out all of these solutions as suboptimal, so it's safe to advance l.

Related

Knapsack problem where there are three arrays: weight of an item array, value of an item array and amount of an item array

What would be the solution to this problem? I think it would be rather tricky because you would have to juggle three characteristics.
Here is my attempt:
p-value, s-weight, k-amount, max-maximum weight,
m-number of items to take
static int solution(int[] s, int[] p, int[] k, int max)
{
int[,] matrix = new int[s.Length + 1, max + 1];
for (int i = 1; i <= s.Length; i++)
{
for (int j = 0; j <= max; j++)
{
int m = 1;
for(int z = k[i - 1]; z > 0; z--)
{
if(z * s[i - 1] <= j)
{
m = z;
break;
}
}
if (s[i - 1] * m <= j)
{
matrix[i, j] =
Math.Max((p[i - 1] * m) + matrix[i - 1, j - s[i - 1] * m],
matrix[i - 1, j]);
}
else
{
matrix[i, j] = matrix[i - 1, j];
}
}
}
return matrix[s.Length, max];
}
For knapsack with some amount of item copies there's a nice trick to reduce it to classical knapsack:
Let's say we have x copies of some item weighting w and giving value v
Notice that it's the same as having log x items where each item has w, 2*w, 4*w, ... weight and v, 2*v, 4*vvalue and sum of all weights is same as w*x
In cases where x can't be expressed as sum of powers of 2 we can make last number what's left (i.e. if x=10, our weight distribution is: 1 + 2 + 4 + 3 = 10). The important thing is to choose such weights that all possible pickings of 1..x are possible to make by picking some subset
After this the problem is reduced to classical knapsack.
Complexity O(#items * #maxWeight * log2(#MaxItemCount)
We can make the dp matrix using the weight array and the amount array.
For the amount array take the max of it (assume K), then make the dp matrix.
dp[n+1][k+1] gives the minimum cost to satisfy a person with capacity k who can eat the first n dishes (where n is the size of the weight array and k is the max amount a person can eat)
the relation afterward will be the same as an unbounded knapsack.

Number of ways to write n as sum of k numbers with restrictions on each part

Title says it all.
I need to split n as sum of k parts where each part ki should be in the range of
1 <= ki <= ri for given array r.
for example -
n = 4, k = 3 and r = [2, 2, 1]
ans = 2
#[2, 1, 1], [1, 2, 1]
Order matters. (2, 1, 1) and (1, 2, 1) are different.
I taught of solving it using stars and bars method, but be because of upper bound ri i dont know to to approach it.
i implemented a direct recursion function and it works fine for small values only.
Constraints of original problem are
1 <= n <= 107
1 <= k <= 105
1 <= ri <= 51
All calculations will be done under prime Modulo.
i found a similar problem here but i don't know how to implement in program. HERE
My brute-force recursive function -
#define MAX 1000
const int md = 1e9 + 7;
vector <int> k;
vector <map<int, int>> mapper;
vector <int> hold;
int solve(int sum, int cur){
if(cur == (k.size() - 1) && sum >= 1 && sum <= k[cur]) return 1;
if(cur == (k.size() - 1) && (sum < 1 || sum > k[cur])) return 0;
if(mapper[cur].find(sum) != mapper[cur].end())
return mapper[cur][sum];
int ans = 0;
int start = 1;
for(int i=start; i<=k[cur]; ++i){
int remain = sum - i;
int seg = (k.size() - cur) - 1;
if(remain < seg) break;
int res = solve(sum - i, cur + 1);
ans = (1LL * ans + res) % md;
}
mapper[cur][sum] = ans;
return ans;
}
int main(){
for(int i=0; i<MAX; ++i) k.push_back(51); // restriction for each part default 51
mapper.resize(MAX);
cout << solve(MAX + MAX, 0) << endl;
}
Instead of using a map for storing result of computation i used a two dimensional array and it gave very good performance boost but i cannot use it because of large n and k values.
How could i improve my recursive function or what are other ways of solving this problem.
That's interesting problem.
First lets say r_i = r_i - 1, n = n - k, numbers in [0, r_i] just for convenience. Now it's possible to add some fictitious numbers to make m the power of 2 without changing answer.
Now let's represent each interval of [0, r_i] as polynomial 1 * x ^ 0 + 1 * x ^ 1 + ... + 1 * x & r_i. Now if we multiply all these polynomials, coefficient at x ^ n will be answer.
Here is structure called Number Theoretic Transform (NTT) which allows to multiply two polynomials modulo p in O(size * log(size)).
If you will just multiply it using NTT, code will work in something like O(n * k * log (k * max(r))). It's very slow.
But now our fictive numbers help. Let's use divide and conquer technics. We'll make O(log m) steps, on each step multiply 2 * i-th and 2 * i + 1-th polynomials. In the next step we'll multiply resulting polynomials of this step.
Each step works in O(k * log(k)) and there is O(log(k)) steps, so algorhitm works in O(k * log^2 (k)). It's fast asymptotically, but I'm not sure if it fits TL for this problem. I think it will work about 20 seconds on max test.

Dynamic programming based zigzag puzzle

I found this interesting dynamic programming problem where it's required to re-order a sequence of integers in order to maximize the output.
Steve has got N liquor bottles. Alcohol quantity of ith bottle is given by A[i]. Now he wants to have one drink from each of the bottles, in such a way that the total hangover is maximised.
Total hangover is calculated as follow (Assume the 'alcohol quantity' array uses 1-based indexing) :
int hangover=0 ;
for( int i=2 ; i<=N ; i++ ){
hangover += i * abs(A[i] - A[i-1]) ;
}
So, obviously the order in which he drinks from each bottle changes the Total hangover. He can drink the liquors in any order but not more than one drink from each bottle. Also once he starts drinking a liquor he will finish that drink before moving to some other liquor.
Steve is confused about the order in which he should drink so that the hangover is maximized. Help him find the maximum hangover he can have, if he can drink the liquors in any order.
Input Format :
First line contain number of test cases T. First line of each test case contains N, denoting the number of fruits. Next line contain N space separated integers denoting the sweetness of each fruit.
2
7
83 133 410 637 665 744 986
4
1 5 9 11
I tried everything that I could but I wasn't able to achieve a O(n^2) solution. By simply calculating the total hangover over all the permutations has a O(n!) time complexity. Can this problem be solved more efficiently?
Thanks!
My hunch: use a sort of "greedy chaining algorithm" instead of DP.
1) find the pair with the greatest difference (O(n^2))
2) starting from either, find successively the next element with the greatest difference, forming a sort of "chain" (2 x O(n^2))
3) once you've done it for both you'll have two "sums". Return the largest one as your optimal answer.
This greedy strategy should work because the nature of the problem itself is greedy: choose the largest difference for the last bottle, because this has the largest index, so the result will always be larger than some "compromising" alternative (one that distributes smaller but roughly uniform differences to the indices).
Complexity: O(3n^2). Can prob. reduce it to O(3/2 n^2) if you use linked lists instead of a static array + boolean flag array.
Pseudo-ish code:
int hang_recurse(int* A, int N, int I, int K, bool* F)
{
int sum = 0;
for (int j = 2; j <= N; j++, I--)
{
int maxdiff = 0, maxidx;
for (int i = 1; i <= N; i++)
{
if (F[i] == false)
{
int diff = abs(F[K] - F[i]);
if (diff > maxdiff)
{
maxdiff = diff;
maxidx = i;
}
}
}
K = maxidx;
F[K] = true;
sum += maxdiff * I;
}
return sum;
}
int hangover(int* A, int N)
{
bool* F = new bool[N];
int maxdiff = 0;
int maxidx_i, maxidx_j;
for (int j = 2; j <= N; j++, I--)
{
for (int i = 1; i <= N; i++)
{
int diff = abs(F[j] - F[i]);
if (diff > maxdiff)
{
maxdiff = diff;
maxidx_i = i;
maxidx_j = j;
}
}
}
F[maxidx_i] = F[maxidx_j] = true;
int maxsum = max(hang_recurse(A, N, N - 1, maxidx_i, F),
hang_recurse(A, N, N - 1, maxidx_j, F));
delete [] F;
return maxdiff * N + maxsum;
}

Given a sequence of n positive integers, find a subsequence of length k such that the sum over the absolute values of the differences is maximal

We are given a sequence of n positive integers, which I will denote as a0, a1, …, an-1. We are also given an integer k, and our task is to:
find a subsequence of length exactly k (denoted as b0, b1, …, bk-1), such that abs(b1 - b0) + abs(b2 - b1) + … + abs(bk-1 - bk-2) is maximal; and
output the sum (no need to output the entire subsequence).
I have been trying to solve this using a dynamic programming approach but all my efforts have been futile.
EDIT: k <= n. The elements in the sequence b must appear in the same order as they appear in a (otherwise, this would be solved by simply finding max, min, ... or min, max, ...).
Example input:
n = 10
k = 3
1 9 2 3 6 1 3 2 1 3
Output:
16 (the subsequence is 1 9 1, and abs(9 - 1) + abs(1 - 9) = 8 + 8 = 16)
Any help / hints would be greatly appreciated.
I managed to solve this problem. Here's the full code:
#include <stdio.h>
#include <stdlib.h>
int abs(int a)
{
return (a < 0) ? -a : a;
}
int solve(int *numbers, int N, int K)
{
int **storage = malloc(sizeof(int *) * N);
int i, j, k;
int result = 0;
for (i = 0; i < N; ++i)
*(storage + i) = calloc(K, sizeof(int));
// storage[i][j] keeps the optimal result where j + 1 elements are taken (K = j + 1) with numbers[i] appearing as the last element.
for (i = 1; i < N; ++i) {
for (j = 1; j < K; ++j) {
for (k = j - 1; k < i; ++k) {
if (storage[i][j] < storage[k][j - 1] + abs(numbers[k] - numbers[i]))
storage[i][j] = storage[k][j - 1] + abs(numbers[k] - numbers[i]);
if (j == K - 1 && result < storage[i][j])
result = storage[i][j];
}
}
}
for (i = 0; i < N; ++i)
free(*(storage + i));
free(storage);
return result;
}
int main()
{
int N, K;
scanf("%d %d", &N, &K);
int *numbers = malloc(sizeof(int) * N);
int i;
for (i = 0; i < N; ++i)
scanf("%d", numbers + i);
printf("%d\n", solve(numbers, N, K));
return 0;
}
The idea is simple (thanks to a friend of mine for hinting me at it). As mentioned in the comment, storage[i][j] keeps the optimal result where j + 1 elements are taken (K = j + 1) with numbers[i] appearing as the last element. Then, we simply try out each element appearing as the last one, taking each possible number of 1, 2, ..., K elements out of all. This solution works in O(k * n^2).
I first tried a 0-1 Knapsack approach where I kept the last element I had taken in each [i][j] index. This solution did not give a correct result in just a single test case, but it worked in O(k * n). I think I can see where it would yield a suboptimal solution, but if anyone is interested, I can post that code, too (it is rather messy though).
The code posted here passed on all test cases (if you can detect some possible errors, feel free to state them).

3-PARTITION problem

here is another dynamic programming question (Vazirani ch6)
Consider the following 3-PARTITION
problem. Given integers a1...an, we
want to determine whether it is
possible to partition of {1...n} into
three disjoint subsets I, J, K such
that
sum(I) = sum(J) = sum(K) = 1/3*sum(ALL)
For example, for input (1; 2; 3; 4; 4;
5; 8) the answer is yes, because there
is the partition (1; 8), (4; 5), (2;
3; 4). On the other hand, for input
(2; 2; 3; 5) the answer is no. Devise
and analyze a dynamic programming
algorithm for 3-PARTITION that runs in
time poly- nomial in n and (Sum a_i)
How can I solve this problem? I know 2-partition but still can't solve it
It's easy to generalize 2-sets solution for 3-sets case.
In original version, you create array of boolean sums where sums[i] tells whether sum i can be reached with numbers from the set, or not. Then, once array is created, you just see if sums[TOTAL/2] is true or not.
Since you said you know old version already, I'll describe only difference between them.
In 3-partition case, you keep array of boolean sums, where sums[i][j] tells whether first set can have sum i and second - sum j. Then, once array is created, you just see if sums[TOTAL/3][TOTAL/3] is true or not.
If original complexity is O(TOTAL*n), here it's O(TOTAL^2*n).
It may not be polynomial in the strictest sense of the word, but then original version isn't strictly polynomial too :)
I think by reduction it goes like this:
Reducing 2-partition to 3-partition:
Let S be the original set, and A be its total sum, then let S'=union({A/2},S).
Hence, perform a 3-partition on the set S' yields three sets X, Y, Z.
Among X, Y, Z, one of them must be {A/2}, say it's set Z, then X and Y is a 2-partition.
The witnesses of 3-partition on S' is the witnesses of 2-partition on S, thus 2-partition reduces to 3-partition.
If this problem is to be solvable; then sum(ALL)/3 must be an integer. Any solution must have SUM(J) + SUM(K) = SUM(I) + sum(ALL)/3. This represents a solution to the 2-partition problem over concat(ALL, {sum(ALL)/3}).
You say you have a 2-partition implementation: use it to solve that problem. Then (at least) one of the two partitions will contain the number sum(ALL)/3 - remove the number from that partion, and you've found I. For the other partition, run 2-partition again, to split J from K; after all, J and K must be equal in sum themselves.
Edit: This solution is probably incorrect - the 2-partition of the concatenated set will have several solutions (at least one for each of I, J, K) - however, if there are other solutions, then the "other side" may not consist of the union of two of I, J, K, and may not be splittable at all. You'll need to actually think, I fear :-).
Try 2: Iterate over the multiset, maintaining the following map: R(i,j,k) :: Boolean which represents the fact whether up to the current iteration the numbers permit division into three multisets that have sums i, j, k. I.e., for any R(i,j,k) and next number n in the next state R' it holds that R'(i+n,j,k) and R'(i,j+n,k) and R'(i,j,k+n). Note that the complexity (as per the excersize) depends on the magnitude of the input numbers; this is a pseudo-polynomialtime algorithm. Nikita's solution is conceptually similar but more efficient than this solution since it doesn't track the third set's sum: that's unnecessary since you can trivially compute it.
As I have answered in same another question like this, the C++ implementation would look something like this:
int partition3(vector<int> &A)
{
int sum = accumulate(A.begin(), A.end(), 0);
if (sum % 3 != 0)
{
return false;
}
int size = A.size();
vector<vector<int>> dp(sum + 1, vector<int>(sum + 1, 0));
dp[0][0] = true;
// process the numbers one by one
for (int i = 0; i < size; i++)
{
for (int j = sum; j >= 0; --j)
{
for (int k = sum; k >= 0; --k)
{
if (dp[j][k])
{
dp[j + A[i]][k] = true;
dp[j][k + A[i]] = true;
}
}
}
}
return dp[sum / 3][sum / 3];
}
Let's say you want to partition the set $X = {x_1, ..., x_n}$ in $k$ partitions.
Create a $ n \times k $ table. Assume the cost $M[i,j]$ be the maximum sum of $i$ elements in $j$ partitions. Just recursively use the following optimality criterion to fill it:
M[n,k] = min_{i\leq n} max ( M[i, k-1], \sum_{j=i+1}^{n} x_i )
Using these initial values for the table:
M[i,1] = \sum_{j=1}^{i} x_i and M[1,j] = x_j
The running time is $O(kn^2)$ (polynomial )
Create a three dimensional array, where size is count of elements, and part is equal to to sum of all elements divided by 3. So each cell of array[seq][sum1][sum2] tells can you create sum1 and sum2 using max seq elements from given array A[] or not. So compute all values of array, result will be in cell array[using all elements][sum of all element / 3][sum of all elements / 3], if you can create two sets without crossing equal to sum/3, there will be third set.
Logic of checking: exlude A[seq] element to third sum(not stored), check cell without element if it has same two sums; OR include to sum1 - if it is possible to get two sets without seq element, where sum1 is smaller by value of element seq A[seq], and sum2 isn't changed; OR include to sum2 check like previous.
int partition3(vector<int> &A)
{
int part=0;
for (int a : A)
part += a;
if (part%3)
return 0;
int size = A.size()+1;
part = part/3+1;
bool array[size][part][part];
//sequence from 0 integers inside to all inside
for(int seq=0; seq<size; seq++)
for(int sum1=0; sum1<part; sum1++)
for(int sum2=0;sum2<part; sum2++) {
bool curRes;
if (seq==0)
if (sum1 == 0 && sum2 == 0)
curRes = true;
else
curRes= false;
else {
int curInSeq = seq-1;
bool excludeFrom = array[seq-1][sum1][sum2];
bool includeToSum1 = (sum1>=A[curInSeq]
&& array[seq-1][sum1-A[curInSeq]][sum2]);
bool includeToSum2 = (sum2>=A[curInSeq]
&& array[seq-1][sum1][sum2-A[curInSeq]]);
curRes = excludeFrom || includeToSum1 || includeToSum2;
}
array[seq][sum1][sum2] = curRes;
}
int result = array[size-1][part-1][part-1];
return result;
}
Another example in C++ (based on the previous answers):
bool partition3(vector<int> const &A) {
int sum = 0;
for (int i = 0; i < A.size(); i++) {
sum += A[i];
}
if (sum % 3 != 0) {
return false;
}
vector<vector<vector<int>>> E(A.size() + 1, vector<vector<int>>(sum / 3 + 1, vector<int>(sum / 3 + 1, 0)));
for (int i = 1; i <= A.size(); i++) {
for (int j = 0; j <= sum / 3; j++) {
for (int k = 0; k <= sum / 3; k++) {
E[i][j][k] = E[i - 1][j][k];
if (A[i - 1] <= k) {
E[i][j][k] = max(E[i][j][k], E[i - 1][j][k - A[i - 1]] + A[i - 1]);
}
if (A[i - 1] <= j) {
E[i][j][k] = max(E[i][j][k], E[i - 1][j - A[i - 1]][k] + A[i - 1]);
}
}
}
}
return (E.back().back().back() / 2 == sum / 3);
}
You really want Korf's Complete Karmarkar-Karp algorithm (http://ac.els-cdn.com/S0004370298000861/1-s2.0-S0004370298000861-main.pdf, http://ijcai.org/papers09/Papers/IJCAI09-096.pdf). A generalization to three-partitioning is given. The algorithm is surprisingly fast given the complexity of the problem, but requires some implementation.
The essential idea of KK is to ensure that large blocks of similar size appear in different partitions. One groups pairs of blocks, which can then be treated as a smaller block of size equal to the difference in sizes that can be placed as normal: by doing this recursively, one ends up with small blocks that are easy to place. One then does a two-coloring of the block groups to ensure that the opposite placements are handled. The extension to 3-partition is a bit complicated. The Korf extension is to use depth-first search in KK order to find all possible solutions or to find a solution quickly.

Resources