summary of the algorithm of K sum - algorithm

It is the well-konw Twelvefold way:
https://en.wikipedia.org/wiki/Twelvefold_way
Where we want to find the number of solutions for following equation:
X1 + X2 + ... + XK = target
from the given array:
vector<int> vec(N);
We can assume vec[i] > 0. There are 3 cases, for example
vec = {1,2,3}, target = 5, K = 3.
Xi can be duplicate and solution can be duplicate.
6 solutions are {1,2,2}, {2,1,2}, {2,2,1}, {1,1,3}, {1,3,1}, {3,1,1}
Xi can be duplicate and solution cannot be duplicate.
2 solutions are {1,2,2}, {1,1,3}
Xi cannot be duplicate and solution cannot be duplicate.
0 solution.
The ides must be using dynamic programming:
dp[i][k], the number of solution of target = i, K = k.
And the iteration relation is :
if(i > num[n-1]) dp[i][k] += dp[i-num[n-1]][k-1];
For three cases, they depend on the runing order of i,n,k. I know the result when there is no restriction of K (sum of any number of variables):
case 1:
int KSum(vector<int>& vec, int target) {
vector<int> dp(target + 1);
dp[0] = 1;
for (int i = 1; i <= target; ++i)
for (int n = 0; n < vec.size(); n++)
if (i >= vec[n]) dp[i] += dp[i - vec[n]];
return dp.back();
}
case 2:
for (int n = 0; n < vec.size(); n++)
for (int i = 1; i <= target; ++i)
case 3:
for (int n = 0; n < vec.size(); n++)
for (int i = target; i >= 1; --i)
When there is additional variable k, do we just simply add the for loop
for(int k = 1; k <= K; k++)
at the outermost layer?
EDIT:
I tried case 1,just add for loop of K most inside:
int KSum(vector<int> vec, int target, int K) {
vector<vector<int>> dp(K+1,vector<int>(target + 1,0));
dp[0][0] = 1;
for (int n = 0; n < vec.size(); n++)
for (int i = 1; i <= target; ++i)
for (int k = 1; k <= K; k++)
{
if (i >= vec[n]) dp[k][i] += dp[k - 1][i - vec[n]];
}
return dp[K][target];
}
Is it true for case 2 and case 3?

In your solution without variable K dp[i] represents how many solutions are there to achieve sum i.
Including the variable K means that we added another dimension to our subproblem. This dimension doesn't necessarily have to be on a specific axis. Your dp array could look like dp[i][k] or dp[k][i].
dp[i][k] means how many solutions to accumulate sum i using k numbers (duplicate or unique)
dp[k][i] means using k numbers how many solutions to accumulate sum i
Both are the same things. Meaning that you can add the loop outside or inside.

Related

Leetcode Target sum of dynamic programming

Given n and target, find the number of combinations of number from [1,2,...,n] adding up to target. The number can be repeatedly picked (1 + 1 + 2 = 4), however the combinations cannot be duplicated ({1,1,2} and {1,2,1} are regard as one combination). e.g.
n = 2, target = 4: {1,1,1,1}, {1,1,2}, {1,3}, {2,2}, so return 4
Since we only need to return the number of combinations, we use dynamic programming as following:
int sum(int n, int target) {
vector<int> dp(target + 1);
dp[0] = 1;
for (int i = 1; i <= target; ++i) {
for (int j = 1; j <= n; j++) {
if (i >= j) dp[i] += dp[i - j];
}
}
return dp.back();
}
However this solution is for duplicated combinations:{1,1,1,1}, {1,1,2}, {1,2,1}, {2,1,1}, {1,3}, {3,1} {2,2}, so return 7.
Do you know how to modify it to remove the duplications?
Simple modification
for (int j = 1; j <= n; j++) {
for (int i = j; i <= target; i++) {
dp[i] += dp[i - j];
}
}
helps to avoid using small values after larger value, so code counts only sorted combinations
Alike question with specific coin nominals instead of 1..n values

algorithm problem, cost of merging list of integers

Let L be a list of positive integers.
We are allowed to merge two elements of L if they have adjacent indices.
The cost of this operation is the sum of both elements.
For example: [1,2,3,4] -> [3,3,4] with a cost of 3.
We are looking for the minimum cost to merge L into one integer.
Is there a fast way of doing this? I came up with this naive recursive approach but that should
be O(n!).
I have noticed that it benefits a lot from memoization so I think there must be a way to avoid trying all possible permutations which will always result in O(n!).
def solveR(l):
if len(l) <= 2:
return sum(l)
else:
return sum(l) + min(solveR(l[1:]), solveR(l[:-1]),
solveR(l[len(l) // 2:]) + solveR(l[:len(l) // 2]))
This is much like this LeetCode problem, but with K = 2. The comments suggest that the time complexity is O(n^3). Here is some C++ code that implements the algorithm:
class Solution {
public:
int mergeStones(vector<int>& stones, int K) {
K = 2;
int N = stones.size();
if((N-1)%(K-1) > 0) return -1;
int sum[N+1] = {0};
for(int i = 1; i <= N; i++)
sum[i] = sum[i-1] + stones[i-1];
vector<vector<int>> dp(N, vector<int>(N,0));
for(int L=K; L<= N; L++)
for(int i=0, j=i+L-1; j<N; i++,j++) {
dp[i][j] = INT_MAX;
for (int k = i; k < j; k += (K-1))
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]);
if ((L-1)%(K-1) == 0)
dp[i][j] += (sum[j+1] - sum[i]); // add sum in [i,j]
}
return dp[0][N-1];
}
};

Invert Arnold's Cat map - negative array indexes

I'm trying to implement Arnold's Cat map for N*N images using the following formula
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
desMatrix[(i + j) % N][(i + 2 * j) % N] = srcMatrix[i][j];
}
}
To invert the process I do:
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
srcMatrix[(j-i) % N][(2*i-j) % N] = destMatrix[i][j];
}
}
Is the implementation correct?
It seems to me that for certain values of j and i I might get negative indexes from (j-i) and (2*i-j); how should I handle those cases, since matrix indexes are only positive?
In general, when a modulo (%) operation needs to work on negative indexes, you can simply add the modulo argument as many times as it's needed. Since
x % N == ( x + a*N ) % N
for all natural a's, and in this case you have i and j constrained in [0, N), then you can write (N + i - j) and ensure that even if i is 0 and j is N-1 (or even N for that matter), the result will always be non-negative. By the same token, (2*N + i - 2*j) or equivalently (i + 2*(N-j)) is always non-negative.
In this case, though, this is not necessary. To invert your map, you would repeat the forward step reversing the assignments. Since the matrix has unary determinant and is area-preserving, you're assured that you'll get all your points eventually (i.e. covering M(i+1) will yield a covering of M(i)).
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
newMatrix[i][j] = desMatrix[(i + j) % N][(i + 2 * j) % N];
}
}
At this point newMatrix and srcMatrix ought to be identical.
(Actually, you're already running the reverse transformation as your forward one. The one I set up to reverse yours is the one commonly used form for the forward transformation).

Max sum in an array with constraints

I have this problem , where given an array of positive numbers i have to find the maximum sum of elements such that no two adjacent elements are picked. The maximum has to be less than a certain given K. I tried thinking on the lines of the similar problem without the k , but i have failed so far.I have the following dp-ish soln for the latter problem
int sum1,sum2 = 0;
int sum = sum1 = a[0];
for(int i=1; i<n; i++)
{
sum = max(sum2 + a[i], sum1);
sum2 = sum1;
sum1 = sum;
}
Could someone give me tips on how to proceed with my present problem??
The best I can think of off the top of my head is an O(n*K) dp:
int sums[n][K+1] = {{0}};
int i, j;
for(j = a[0]; j <= K; ++j) {
sums[0][j] = a[0];
}
if (a[1] > a[0]) {
for(j = a[0]; j < a[1]; ++j) {
sums[1][j] = a[0];
}
for(j = a[1]; j <= K; ++j) {
sums[1][j] = a[1];
}
} else {
for(j = a[1]; j < a[0]; ++j) {
sums[1][j] = a[1];
}
for(j = a[0]; j <= K; ++j) {
sums[1][j] = a[0];
}
}
for(i = 2; i < n; ++i) {
for(j = 0; j <= K && j < a[i]; ++j) {
sums[i][j] = max(sums[i-1][j],sums[i-2][j]);
}
for(j = a[i]; j <= K; ++j) {
sums[i][j] = max(sums[i-1][j],a[i] + sums[i-2][j-a[i]]);
}
}
sums[i][j] contains the maximal sum of non-adjacent elements of a[0..i] not exceeding j. The solution is then sums[n-1][K] at the end.
Make a copy (A2) of the original array (A1).
Find largest value in array (A2).
Extract all values before the it's preceeding neighbour and the values after it's next neighbour into a new array (A3).
Find largest value in the new array (A3).
Check if sum is larger that k. If sum passes the check you are done.
If not you will need to go back to the copied array (A2), remove the second larges value (found in step 3) and start over with step 3.
Once there are no combinations of numbers that can be used with the largest number (i.e. number found in step 1 + any other number in array is larger than k) you remove it from the original array (A1) and start over with step 0.
If for some reason there are no valid combinations (e.g. array is only three numbers or no combination of numbers are lower than k) then throw an exception or you return null if that seems more appropriate.
First idea: Brute force
Iterate all legal combination of indexes and build the sum on the fly.
Stop with one sequence when you get over K.
keep the sequence until you find a larger one, that is still smaller then K
Second idea: maybe one can force this into a divide and conquer thing ...
Here is a solution to the problem without the "k" constraint which you set out to do as the first step: https://stackoverflow.com/a/13022021/1110808
The above solution can in my view be easily extended to have the k constraint by simply amending the if condition in the following for loop to include the constraint: possibleMax < k
// Subproblem solutions, DP
for (int i = start; i <= end; i++) {
int possibleMaxSub1 = maxSum(a, i + 2, end);
int possibleMaxSub2 = maxSum(a, start, i - 2);
int possibleMax = possibleMaxSub1 + possibleMaxSub2 + a[i];
/*
if (possibleMax > maxSum) {
maxSum = possibleMax;
}
*/
if (possibleMax > maxSum && possibleMax < k) {
maxSum = possibleMax;
}
}
As posted in the original link, this approach can be improved by adding memorization so that solutions to repeating sub problems are not recomputed. Or can be improved by using a bottom up dynamic programming approach (current approach is a recursive top down approach)
You can refer to a bottom up approach here: https://stackoverflow.com/a/4487594/1110808

Maximum Countiguous Negative Sum or Mnimum positive subsequence sum problem

We all heard of bentley's beautiful proramming pearls problem
which solves maximum subsequence sum:
maxsofar = 0;
maxcur = 0;
for (i = 0; i < n; i++) {
maxcur = max(A[i] + maxcur, 0);
maxsofar = max(maxsofar, maxcur);
}
What if we add an additional condition maximum subsequence that is lesser M?
This should do this. Am I wright?
int maxsofar = 0;
for (int i = 0; i < n - 1; i++) {
int maxcur = 0;
for (int j = i; j < n; j++) {
maxcur = max(A[j] + maxcur, 0);
maxsofar = maxcur < M ? max(maxsofar, maxcur) : maxsofar;
}
}
Unfortunately this is O(n^2). You may speed it up a little bit by breaking the inner loop when maxcur >=M, but still n^2 remains.
This can be solved using dynamic programming albeit only in pseudo-polynomial time.
Define
m(i,s) := maximum sum less than s obtainable using only the first i elements
Then you can calculate max(n,M) using the following recurrence relation
m(i,s) = max(m(i-1,s), m(i-1,s-A[i]]+A[i]))
This solution is similar to the solution to the knapsack problem.
If all A[i] > 0, you can do this in O(n lg n): precompute partial sums S[i], then binary search S for S[i] + M. For instance:
def binary_search(L, x):
def _binary_search(lo, hi):
if lo >= hi: return lo
mid = lo + (hi-lo)/2
if x < L[mid]:
return _binary_search(lo, mid)
return _binary_search(mid+1, hi)
return _binary_search(0, len(L))
A = [1, 2, 3, 2, 1]
M = 4
S = [A[0]]
for a in A[1:]:
S.append(S[-1] + a)
maxsum = 0
for i, s in enumerate(S):
j = binary_search(S, s + M)
if j == len(S):
break
sum = S[j-1] - S[i]
maxsum = max(sum, maxsum)
print maxsum
EDIT: as atuls correctly points out, the binary search is overkill; since S is increasing, we can just keep track of j each iteration and advance from there.
Solveable in O(n log(n)). Using a binary search tree (balanced) to search for smallest value larger than sum-M, and then update min, and insert sum, by going from left to right. Where sum is the partial sum so far.
best = -infinity;
sum = 0;
tree.insert(0);
for(i = 0; i < n; i++) {
sum = sum + A[i];
int diff = sum - tree.find_smallest_value_larger_than(sum - M);
if (diff > best) {
best = diff;
}
tree.insert(sum);
}
print best

Resources