相反的问题:
具有正和<= K的最大长度子序列实际上是标准的01背包问题。
它的解决方案非常简单:
int solve(const vector<int> &A, int K) {
int dp[A.size()+1][K+1];
int i, j;
// Base Cases
for(i=0; i<= K; i++)
dp[0][i] = 0;
for(i=0; i<= A.size(); i++)
dp[i][0] = 0;
for(i=1; i <= A.size(); i++)
{
for(j=1; j<= K; j++)
{
dp[i][j] = dp[i-1][j];
if(A[i-1] <= j)
dp[i][j] = max(dp[i][j], 1 + dp[i-1][j-A[i-1]]);
}
}
return dp[A.size()][K]
我很难想到如何将sum <= K的最小长度子序列沿着相同的线实现。
例:
A = [14, 10, 4]
K = 14
Minimum Length Subsequence = 14
Maximum Length Subsequence = 10, 4 (Arrived from Knapsack)
它肯定不像将max更改为min那么容易,因为答案应始终是基本情况。这让我想到,我们需要调整基础案例吗?我被困在这里,需要一些推动力。
关于如何解决这个问题的任何想法?
用有序对(sum, length)
替换总和。现在应用您知道的先前算法。顺序是词典,按总和然后按长度。你正试图接近(target_sum, 0)
。
现在最接近的“和”将是最短的子序列,具有最小的正差。
下面的代码片段显示了筛网的含义。这是一个简单的解决方案,可能对大输入无用。它不像筛子那样找到素数,它只包含真或假,但更像是组合字典和它们的总和,例如:
{value: 14, components: [4, 10]}
如果您不熟悉Javascript,则数组的行为更像是关联数组或带字符串键的字典(这就是需要进行Number
转换的原因),而for in
只会迭代具有值的元素(如果数组是稀疏的)。此外,slice
和concat
创建了数组的副本。
function minSub(array, target) {
var sieve = [[]];
for (var i in array) {
var temp = [];
for (var j in sieve) {
var val = Number(j) + array[i];
if (!sieve[val] || sieve[val].length > sieve[j].length + 1) {
temp[val] = sieve[j].concat([array[i]]);
}
}
for (var j in temp) {
if (Number(j) <= target) {
sieve[j] = temp[j].slice();
}
}
}
var max = 0;
for (var j in sieve) {
if (Number(j) > max) {
max = Number(j);
}
}
return sieve[max];
}
console.log(minSub([4, 10, 14], 14));
console.log(minSub([0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0], 8));
请注意,与我在评论中建议的相反,按降序对输入进行排序并不能保证首先找到形成值的最简单组合;每当遇到筛子中已存在的值时,您必须检查组件的数量;例如输入:
{8, 4, 3, 2, 1}
你会发现这个组合:
{value: 9, components: [4, 3, 2]}
在找到之前:
{value: 9, components: [8, 1]}
我认为这与你所寻找的一致。在检查是否可以达到总和时,我们必须比在最大子序列的制定中更加小心。在这个公式中,dp[i][j]
是与j
相加的最小子序列,考虑到A[i]
的元素(所以i
不是子序列长度)。
JavaScript代码(仅经过轻微测试):
function solve(A, K) {
let i,j;
let dp = new Array(length);
for (i=0; i<A.length; i++)
dp[i] = new Array(K + 1);
// Base Cases
for(i=0; i<A.length; i++)
dp[i][0] = 0;
for (i=0; i<A.length; i++){
// Exact match
if (A[i] == K)
return 1;
// We can reach this sum with only one element
if (A[i] < K)
dp[i][A[i]] = 1;
// There are no previously achieved sums
if (i == 0)
continue;
for (j=1; j<=K; j++){
dp[i][j] = dp[i][j] || dp[i - 1][j];
if (A[i] <= j){
dp[i][j] = Math.min(
dp[i][j] || Infinity,
1 + (dp[i - 1][j - A[i]] || Infinity)
);
}
}
}
for (i=K; i>=0; i--)
if (![undefined, Infinity].includes(dp[A.length - 1][i]))
return dp[A.length - 1][i];
}
console.log(solve([1,2,3,4,5,6,7,8,9,10], 11));
console.log(solve([14,10,4], 14));
console.log(solve([0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0], 8));
console.log(solve([7,7,2,3],15))