Here is a working solution in C++ that runs in O(logn * n * k) time with O(n*k) space. I think you can not make it faster but let me know if you find a faster solution. This is a modification of the solution for from https://stackoverflow.com/questions/16402854/number-of-increasing-subsequences-of-length-k. The key difference here is that we keep track of the maximum sum for each subsequences of different legths instead of accumulating the number of subsequences and we are iterating from the back of the array (since for increasing subsequences that have length larger than k the best k-length subarray will be at the end).
An other trick is that we use the array sums to map index + length combinations to maximum sums.
maxSumIncreasingKLenSeqDP function is the simple dynamic programming solution with O(n * n * k) time complexity.
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <limits.h>
using namespace std;
#include <random>
int maxSumIncreasingKLenSeq(int arr[], size_t n, int k){
// inverse compression: assign N-1, N-2, ... , 1 to smallest, ..., largest
size_t N = 1;
size_t compArr[n];
{
for(size_t i = 0; i<n; ++i)
compArr[i] = arr[i];
// descending order
sort(compArr, compArr + n, greater<int>());
unordered_map<int, size_t> compMap;
for(int val : compArr){
if(compMap.find(val) == compMap.end()){
compMap[val] = N;
++N;
}
}
for(size_t i = 0; i<n; ++i)
compArr[i] = compMap[arr[i]];
}
int sums[n * (k - 1) + n]; // key is combined from index and length by n * (length - 1) + index
for(size_t i = 0; i < n * (k - 1) + n; ++i)
sums[i] = -1;
for(size_t i = 0; i < n; ++i)
sums[i] = arr[i]; // i, 1
int BIT[N];
for(size_t len = 2; len <= k; ++len){
for(size_t i = 0; i<N; ++i)
BIT[i] = INT_MIN;
for(size_t i = 0; i < len - 1; ++i)
sums[n * (len - 1) + i] = INT_MIN;
for(int i = n - len; i >= 0; --i){
int val = sums[n * (len - 2) + i + 1]; // i + 1, len - 1
int idx = compArr[i + 1];
while(idx <= N){
BIT[idx] = max(val, BIT[idx]);
idx += (idx & (-idx));
}
// it does this:
//BIT[compArr[i + 1]] = sums[n * (len - 2) + i + 1];
idx = compArr[i] - 1;
int maxSum = INT_MIN;
while(idx > 0){
maxSum = max(BIT[idx], maxSum);
idx -= (idx & (-idx));
}
sums[n * (len - 1) + i] = maxSum;
// it does this:
//for(int j = 0; j < compArr[i]; ++j)
// sums[n * (len - 1) + i] = max(sums[n * (len - 1) + i], BIT[j]);
if(sums[n * (len - 1) + i] > INT_MIN)
sums[n * (len - 1) + i] += arr[i];
}
}
int maxSum = INT_MIN;
for(int i = n - k; i >= 0; --i)
maxSum = max(maxSum, sums[n * (k - 1) + i]); // i, k
return maxSum;
}
int maxSumIncreasingKLenSeqDP(int arr[], int n, int k){
int sums[n * (k - 1) + n]; // key is combined from index and length by n * (length - 1) + index
for(size_t i = 0; i < n; ++i)
sums[i] = arr[i]; // i, 1
for(int i = 2; i <= k; ++i)
sums[n * (i - 1) + n - 1] = INT_MIN; // n - 1, i
// moving backward since for increasing subsequences it will be the last k items
for(int i = n - 2; i >= 0; --i){
for(size_t len = 2; len <= k; ++len){
int idx = n * (len - 1) + i; // i, length
sums[idx] = INT_MIN;
for(int j = n - 1; j > i; --j){
if(arr[i] < arr[j])
sums[idx] = max(sums[idx], sums[n * (len - 2) + j]); // j, length - 1
}
if(sums[idx] > INT_MIN)
sums[idx] += arr[i];
}
}
int maxSum = INT_MIN;
for(int i = n - k; i >= 0; --i)
maxSum = max(maxSum, sums[n * (k - 1) + i]); // i, k
return maxSum;
}
int main(){
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist(1,10);
for(int len = 3; len < 10; ++len){
for(int i = 0; i < 10000; ++i){
int arr[100];
for(int n = 0; n < 100; ++n)
arr[n] = dist(rng);
int res = maxSumIncreasingKLenSeqDP(arr, 100, len);
int fastRes = maxSumIncreasingKLenSeq(arr, 100, len);
if(res != fastRes)
cout << "failed" << endl;
else
cout << "passed" << endl;
}
}
return 0;
}