相反的问题:
具有正和<= 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]
我在思考如何以相同的方式实现总和<= K 的最小长度子序列时遇到了困难。
示例:
A = [14, 10, 4]
K = 14
Minimum Length Subsequence = 14
Maximum Length Subsequence = 10, 4 (Arrived from Knapsack)
肯定不像将max更改为min那样容易,因为答案始终是基本情况。这使我想到:我们需要调整基本情况吗?我被困在这里,需要一些推动。
关于应该如何解决此问题的任何想法?
答案 0 :(得分:1)
用有序对(sum, length)
替换总和。现在应用您知道的先前算法。顺序是字典式的,先求和再求长度。您正试图接近(target_sum, 0)
。
现在最接近的“和”将是具有最小正差的最短子序列。
答案 1 :(得分:1)
下面的代码片段显示了筛子的含义。这是一个简单的解决方案,可能对大量输入没有用。这不是像筛子那样查找素数,后者只包含true或false,而更像是组合及其和的字典,例如:
{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]}
答案 2 :(得分: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))