我觉得我已经给了很多想法,并尝试了很多东西。我设法解决它,并产生正确的值,但问题是它没有足够的时间效率。它完成了Kattis测试中的2次,并且由于时间限制超过1秒而在3上失败。现在我可以看到他们测试的输入是什么,我害怕。
我从一个递归的解决方案开始并完成了。但后来我才意识到它没有足够的时间效率,所以我试图转向迭代解决方案。
我首先阅读输入并将其添加到ArrayList。然后我调用以下方法,目标为1000。
public static int getCorrectWeight(List<Integer> platesArr, int target) {
/* Creates two lists, one for storing completed values after each iteration,
one for storing new values during iteration. */
List<Integer> vals = new ArrayList<>();
List<Integer> newVals = new ArrayList<>();
// Inserts 0 as a first value so that we can start the first iteration.
int best = 0;
vals.add(best);
for(int i=0; i < platesArr.size(); i++) {
for(int j=0; j < vals.size(); j++) {
int newVal = vals.get(j) + platesArr.get(i);
if (newVal <= target) {
newVals.add(newVal);
if (newVal > best) {
best = newVal;
}
} else if ((Math.abs(target-newVal) < Math.abs(target-best)) || (Math.abs(target-newVal) == Math.abs(target-best) && newVal > best)) {
best = newVal;
}
}
vals.addAll(newVals);
}
return best;
}
我的问题是,是否有某种方法可以减少大量数据的时间复杂度?
答案 0 :(得分:1)
主要问题是vals
和newVals
的大小可以非常快速地增长,因为每次迭代可以使其大小加倍。您只需要存储1000个左右的值,这些值应该是可管理的。您限制了这些值,但由于它们存储在ArrayList
中,因此最终会产生大量重复值。
相反,如果您使用了HashSet
,那么它应该会提高效率。
答案 1 :(得分:0)
您只需要存储大小为2001(0到2000)的DP表
让dp[i]
表示是否可以形成i
kg的权重。如果权重超过数组边界,则忽略它。
例如:
dp[0] = 1;
for (int i = 0; i < values.size(); i++){
for (int j = 2000; j >= values[i]; j--){
dp[j] = max(dp[j],dp[j-values[i]);
}
}
此处,values
是存储所有原始权重的位置。除dp
之外,dp[0]
的所有值都将设置为0。
然后,检查1000是否可以制作它。如果没有,请检查999和1001,依此类推。
这应该在O(1000n + 2000)
时间内运行,因为n
最多为1000,这应该及时运行。
顺便说一句,这是一个改进的背包算法,您可能想要查找其他一些变体。
答案 2 :(得分:0)
如果您对这类问题的考虑过于笼统,您可能认为必须检查所有可能的输入组合(每个重量都可以包含或排除),为您提供2个 n 组合来测试如果你有n个输入。然而,这与此无关。相反,这里的关键是所有权重都是整数,目标是1000.
让我们首先检查角落情况,因为这会限制搜索空间。
如果所有权重都是> = 1000,则选择最小权重。
如果至少有一个重量<1。 1000,总是优于任何重量&gt; = 2000,因此您可以忽略任何重量&gt; = 1000用于组合目的。
然后,应用动态编程。保留一组(你的HashSet作为其他海报的建议,但BitSet甚至更好,因为它的最大值是如此之小)所有组合的前k个输入,并通过组合所有先前的解与k + 1来增加k '输入。
当您考虑所有可能性时,只需搜索位向量以获得最佳响应。
static int count() {
int[] weights = new int[]{900, 500, 498, 4};
// Check for corner case to limit search later
int min = Integer.MAX_VALUE;
for (int weight : weights) min = Math.min(min, weight);
if (min >= 1000) {
return min;
}
// Get all interesting combinations
BitSet combos = new BitSet();
for (int weight : weights) {
if (weight < 1000) {
for (int t = combos.previousSetBit(2000 - weight) ; t >= 0; t = combos.previousSetBit(t-1)) {
combos.set(weight + t);
}
combos.set(weight);
}
}
// Pick best combo
for (int distance = 0; distance <= 1000; distance++) {
if (combos.get(1000 + distance)) {
return 1000 + distance;
}
if (combos.get(1000 - distance)) {
return 1000 - distance;
}
}
return 0;
}