这个问题来自于编程竞赛,我无法在可接受的时间内解决它。
您将获得a
个n
整数的数组s
。找到不超过给定整数k
的确切m (s < m)
元素(不一定是连续)的最大总和 0 < k <= n < 100
m < 3000
0 < a[i] < 100
。
约束:
{{1}}
信息:保证给定输入的解决方案。
现在,我想我最好的选择是DP方法,但无法提出正确的公式。
答案 0 :(得分:2)
我会尝试两件事。它们都基于以下想法:
如果我们可以解决决定是否有k
个元素将完全与p
相加的问题,那么我们可以在[1, m]
中二进制搜索答案}。
<强> 1。优化的暴力
当当前总和超过p
时,只需对数组进行排序并缩短搜索范围。这个想法是你通常只需要回溯很少,因为排序的数组应该有助于尽早消除不良解决方案。
说实话,我怀疑这会足够快。
<强> 2。随机算法
保留used
大小为k
的数组。随机分配元素。虽然它们的总和不是p
,但是用另一个元素随机替换一个元素,并确保在恒定时间内更新它们的总和。
最多保持e
次这样做(尝试使用其最佳结果值,最后复杂度为O(e log m)
,因此它可能会非常高),如果你不能在此期间得到总和p
,假设它不可能。
或者,忘记二进制搜索。直接运行随机算法并返回它在e
运行中找到的最大有效总和,或者直到您分配的运行时间结束。
我不确定DP如何有效地跟踪总和中使用的元素数量。我认为随机算法值得一试,因为它很容易实现。
答案 1 :(得分:2)
两种可接受的方法都不如人意。此外,这不是DP可以解决的问题类型。
以下是通过示例说明的正确方法:
想象a = {2,3,5,9,11,14,17,23}(因此n = 8),k = 3,s = 30
对数组进行排序a。
定义三个指向数组的指针,P1,P2和P3从1到n。 P1&lt; P2&lt; P3
将P3设置为a_max(此处为23),P1设置为1,P2设置为2.计算总和s(此处为23 + 2 + 3 = 28)。如果s> S,然后将P3减一,再试一次,直到找到解决方案。如果P3&lt; 3,那就没有解决方案了。将您的第一个解决方案存储为迄今为止最着名的解决方案(BKSSF)。
接下来,增加P2直到s> S.如果您找到更好的解决方案更新BKSSF。将P2减少一个。
接下来增加P1直到s> S.如果您找到更好的解决方案,请更新。
现在回到P2并将其减少一个。
然后增加P1直到s> S.等。
你可以看到这是一个递归算法,其中每增加或减少一个或多个相应的减少,增加。
此算法将比上述尝试快得多。
答案 2 :(得分:0)
表示l&lt; = k且r&lt; = s:
V [l] [r] =如果可以精确选择总计 r 的 l 元素。
V[0][0] = true
for i in 1..n:
V'[][] - initialize with false
for l in 0..k-1:
for r in 0..s:
if V[l][r] and s + a[i] <= s:
V'[l + 1][r + a[i]] = true
V |= V'
这给你所有可实现的总和O(k * n * s)。
答案 3 :(得分:0)
我认为Tyler Durden有正确的想法。但是你不必总结所有元素,你基本上可以贪婪地做,所以你可以减少循环。在C ++中:
#include <iostream>
#include <algorithm>
using namespace std;
#define FI(n) for(int i=0;i<(n);i++)
int m, n, k;
int a[] = { 12, 43, 1, 4, 3, 5, 13, 34, 24, 22, 31 },
e[20];
inline int max(int i) { return n-k+i+1; }
void print(int e[], int ii, int sum)
{ cout << sum << '\t';
FI(ii+1) cout << e[i]<<','; cout<<'\n';
}
bool desc(int a, int b) { return a>b; }
int solve()
{ sort(a, a+n, desc);
cout <<"a="; FI(n) cout << a[i]<<','; cout<<"\nsum\tindexes\n";
int i,sum;
i = e[0] = sum = 0;
print (e,i,a[0]);
while(1)
{ while (e[i]<max(i) && sum+a[e[i]]>=m) e[i]++;
if (e[i]==max(i))
{ if (!i) return -1; // FAIL
cout<<"*"; print (e,i,sum);
sum -= a[e[--i]++];
} else // sum+a[e[i]]<m
{ sum += a[e[i]];
print (e,i,sum);
if (i+1==k) return sum;
e[i+1] = e[i]+1;
i++;
}
}
}
int main()
{ n = sizeof(a)/sizeof(int);
k = 3;
m = 39;
cout << "n,k,m="<<n<<' '<<k<<' '<<m<<'\n';
cout << solve();
}
对于m = 36,它给出了输出
n,k,m=11 3 36
a=43,34,31,24,22,13,12,5,4,3,1,
sum indexes
43 0,
34 1,
*34 1,10,
31 2,
35 2,8,
*35 2,8,11,
34 2,9,
35 2,9,10,
35
对于m = 37,它给出了
n,k,m=11 3 37
a=43,34,31,24,22,13,12,5,4,3,1,
sum indexes
43 0,
34 1,
*34 1,10,
31 2,
36 2,7,
*36 2,7,11,
35 2,8,
36 2,8,10,
36
(最后一次尝试:对于m = 39,它也给出正确答案,38) 输出:最后一个数字是总和,它上面的行有索引。带有星号的行在回溯之前,因此该行的最后一个索引太高。运行时应为O(k * n)。
对于难以理解的代码感到抱歉。我可以清理它并根据要求提供解释,但我现在有另一个项目;)。