我尝试用贪婪算法解决“不同求和”问题
问题描述
任务。这个问题的目的是将给定的正整数表示为成对的和
尽可能使用不同的正整数。也就是说,找到最大值使得
可以写成
1 + 2 + · · · +
,其中1, . . . ,
是正整数,所有 ̸=
的{{1}}
输入格式。输入由单个整数组成。
约束条件。 1 ≤ < ≤ .
。
输出格式。在第一行中,输出最大数量1 ≤ ≤ 10^9
,以使可以表示为总和
的成对截然不同的正整数。在第二行中,输出
对成对的不同正整数
总计为
(如果有很多这样的表示,请输出其中任何一种)。
我的代码:
如果输入太大,我的代码将失败,大约需要3.24秒,并且最大可用时间为1.5秒。
答案 0 :(得分:1)
在ArrayList(summands变量)上执行contains
时,它将遍历列表中的所有值以查找该项目是否已经存在。 O(n)操作。
尝试使用HashSet而不是列表,以获得更好的性能O(1)。
如果您关心结果(求和)中项目的顺序,则可以使用LinkedHashSet。
答案 1 :(得分:1)
至少有 k 个不同的求和项可以生成的最小数字就是从 1 到 k 的所有数字的总和。小于此数字的任何个数将最多使 个 k-1 。
Gauss具有一个公式,用于计算从 1 到 k 的数字之和。只是 k(k + 1)/ 2 。
您只需要找到最大的 k ,以使 k(k + 1)/ 2 <= n 。从上面可以知道,如果 k 更大,则无法将 n 划分为这么多求和数,因此这是最大的答案。
实际生成加到 n 的 k 个求和也很容易-这只是从 1 到< strong> k-1 ,然后剩下的值( n-k(k-1)/ 2 )。
您可以直接求解 k :
k(k + 1)/ 2 <= n
k²+ k-2n <= 0
k <=(sqrt(8n + 1)-1)/ 2
最后一步是通过二次公式。由于您想要最大的 k ,所以就
k = floor((sqrt(8n + 1)-1)/ 2)
答案 2 :(得分:0)
即使我们可以使用HashSet
来解决它,但也可以不用它。
我们知道summands
具有从1
到start
的连续数字。因此,请注意,如果不是newNumber-start <= start
,则必须停止操作,而不是检查列表。因此,newNumber
应该是我们答案的最后一个数字。
通过最小化更改相应地修改代码,
...
while (true) {
if (newNumber-start <= start) { // possibly (newNumber <= start*2)
summands.add(newNumber);
newNumber = 0;
} else {
...
现在整体时间复杂度为O(n^0.5)
。 (搜索是O(n) = O(n^0.5 * n^0.5)
,搜索List
)
答案 3 :(得分:0)
在测试我的代码之后,我发现问题不仅在于代码性能,还在于代码有错误,但是使用HashSet确实很棒,它比ArrayList匹配的速度更快,这是我的新代码
我的代码
private static HashSet<Integer> optimalSummands(int n) {
HashSet<Integer> summands = new HashSet<>();
int start = 1;
int newNumber = n;
if (n == 2) {
summands.add(2);
return summands;
}
while (true) {
if (newNumber < 0) {
Object[] value = summands.toArray();
int secondlast = (int) value[summands.size() - 2];
int last = (int) value[summands.size() - 1];
summands.remove(last);
summands.remove(secondlast);
newNumber = secondlast + last -1;
}
if (summands.contains(newNumber - start) ) {
start++;
continue;
} else {
newNumber -= start;
summands.add(start);
start++;
}
if (newNumber == 0) {
return summands;
}
}
}