我有一个数字n,我必须将其分成k个数,使得所有k个数字都是不同的,k个数的总和等于n,k是最大值。例如,如果n是9,那么答案应该是1,2,6。如果n是15,那么答案应该是1,2,3,4,5 这就是我尝试过的 -
void findNum(int l, int k, vector<int>& s)
{
if (k <= 2 * l) {
s.push_back(k);
return;
}
else if (l == 1) {
s.push_back(l);
findNum(l + 1, k - 1, s);
}
else if(l == 2) {
s.push_back(l);
findNum(l + 2, k - 2, s);
}
else{
s.push_back(l);
findNum(l + 1, k - l, s);
}
}
最初k = n且l = 1.结果数字存储在s中。该解决方案即使将数字n作为k个不同数字的总和返回,但它不是最优解(k不是最大值)。 n = 15的示例输出是1,2,4,8。应该做出哪些改变才能得到正确的结果?
答案 0 :(得分:6)
Greedy算法适用于此问题。刚开始总结从1到m
,sum(1...m) <= n
。一旦超过,将多余的部分添加到m-1
。从1到m | m-1的数字将是答案。
例如
18
1+2+3+4+5 < 18
+6 = 21 > 18
So, answer: 1+2+3+4+(5+6-(21-18))
28
1+2+3+4+5+6+7 = 28
So, answer: 1+2+3+4+5+6+7
伪代码(在恒定时间内,复杂度为O(1))
Find k such that, m * (m+1) > 2 * n
Number of terms = m-1
Terms: 1,2,3...m-2,(m-1 + m - (sum(1...m) - n))
答案 1 :(得分:0)
sum 可以在 {1, ... , m} 中划分为 k 个项,如果 min(k) <= sum <= max(k,m), with
min(k) = 1 + 2 + .. + k = (k*(k+1))/2
max(k,m) = m + (m-1) + .. + (m-k+1) = k*m - (k*(k-1))/2
因此,您可以使用以下伪代码:
fn solve(n, k, sum) -> set or error
s = new_set()
for m from n down to 1:
# will the problem be solvable if we add m to s?
if min(k-1) <= sum-m <= max(k-1, m-1) then
s.add(m), sum-=m, k-=1
if s=0 and k=0 then s else error()