如何使用动态规划来查找一个数组中正整数列表的总和最接近但不等于某个正整数K?
在思考这个问题时,我有些困惑。
如何使用动态规划来查找一个数组中正整数列表的总和最接近但不等于某个正整数K?
在思考这个问题时,我有些困惑。
#include <iostream>
#include <functional>
#define elements(array) (sizeof(array)/sizeof(array[0]))
int main() {
// Since we're assuming subscripts from 1..N, I've inserted a dummy value
// for v[0].
int v[] = {0, 7, 15, 2, 1};
// For the moment I'm assuming a maximum <= MAX.
const int MAX = 17;
// ... but if you want to specify K as the question implies, where sum<K,
// you can get rid of MAX and just specify K directly:
const int K = MAX + 1;
const int rows = elements(v);
int table[rows][K] = {0};
bool used[rows][K] = {false};
for (int i=1; i<rows; i++)
for (int c = 0; c<K; c++) {
int prev_val = table[i-1][c];
int new_val;
// we compute new_val inside the if statement so we won't
// accidentally try to use a negative column from the table if v[i]>c
if (v[i] <= c && (new_val=v[i]+table[i-1][c-v[i]]) > prev_val) {
table[i][c] = new_val;
used[i][c] = true;
}
else
table[i][c] = prev_val;
}
std::cout << "Result: " << table[rows-1][MAX] << "\n";
std::cout << "Used items where:\n";
int column = MAX;
for (int i=rows; i>-1; i--)
if (used[i][column]) {
std::cout << "\tv[" << i << "] = " << v[i] << "\n";
column -= v[i];
}
return 0;
}
在这个问题中,有几件事情通常需要优化(但为了可读性而没有这样做)。首先,如果你达到了最优解,就可以停止搜索,因此在这种情况下,我们实际上可以在考虑最终输入值为1
之前就退出循环(因为15
和2
可以得到所需的结果17
)。
其次,在表格中,我们实际上只会同时使用两行:当前行和前一行。在主表中之前的行不会再被使用(即计算row[n]
时,我们需要row[n-1]
的值,但不需要row[n-2]
、row[n-3]
……row[0]
。为了减少存储空间,我们可以将主表设置为只有两行,并在第一行和第二行之间切换。一个类似C语言的技巧是只使用行号的最低有效位,因此你可以用table[i&1]
和table[(i-1)&1]
分别替换table[i]
和table[i-1]
(仅适用于主表,不适用于访问used
表时)。
以下是Python的一个示例:
def closestSum(a,k):
s={0:[]}
for x in a:
ns=dict(s)
for j in s:
ns[j+x]=s[j]+[x]
s=ns
if k in s:
del s[k]
return s[min(s,key=lambda i:abs(i-k))]
例子:
>>> print closestSum([1,2,5,6],10)
[1, 2, 6]
这个想法就是在遍历数组时跟踪可以从所有先前元素中得出的总和,以及一种生成该总和的方法。最后,你只需要选择最接近你想要的总和即可。这是一种动态规划解决方案,因为它将整体问题分解为子问题,并使用表格记住子问题的结果,而不是重新计算它们。
Cato在Racket中的想法:
#lang racket
(define (closest-sum xs k)
(define h (make-hash '([0 . ()])))
(for* ([x xs] [(s ys) (hash-copy h)])
(hash-set! h (+ x s) (cons x ys))
(hash-set! h x (list x)))
(when (hash-ref h k #f) (hash-remove! h k))
(cdr (argmin (λ (a) (abs (- k (car a)))) (hash->list h))))
(define (closest-sum xs k)
(define h {make '([0 . ()])})
(for* ([x xs] [(s ys) {copy h}])
{! h (+ x s) (cons x ys)}
{! h x (list x)})
(when {h k #f} {remove! h k})
(cdr (argmin (λ (a) (abs (- k (car a)))) {->list h})))