迭代和递归解决方案的时间复杂度

时间:2016-09-17 19:51:14

标签: java algorithm optimization iteration time-complexity

我正在尝试解决以下问题: Programming problem

我觉得我已经给了很多想法,并尝试了很多东西。我设法解决它,并产生正确的值,但问题是它没有足够的时间效率。它完成了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;
}

我的问题是,是否有某种方法可以减少大量数据的时间复杂度?

3 个答案:

答案 0 :(得分:1)

主要问题是valsnewVals的大小可以非常快速地增长,因为每次迭代可以使其大小加倍。您只需要存储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;
}