警卫和需求

时间:2013-02-04 12:24:29

标签: algorithm dynamic-programming

你有N个警卫在一条线上,每个都有硬币需求。只有当他的要求低于你到达他之前已经完全支付的费用时,你才可以跳过支付警卫。找到你花在跨越所有警卫的最少数量的硬币。

我认为这是一个DP问题,但无法提出一个公式。另一种方法是对答案进行二元搜索,但如何验证是否有多个硬币是可能的答案?

3 个答案:

答案 0 :(得分:9)

这确实是一个动态的编程问题。

考虑功能f(i, j),如果有第一个true警卫的分配给你i费用j(1)。您可以在大小为f(i, j)的表格中安排功能n x S,其中S是所有警卫需求的总和。

我们将d_i表示为后卫i的要求。 只要f(i+1)扫描f(i)并将f(i)指定为f(i+1, j + d_i),如果f(i + 1, j)为真且j < d_i,您就可以轻松计算列f(i + 1, j) ,或j >= d_i如果O(nS)

这会在O(S)时间和n空间中运行(您每次只需要保留两列),这只是假多项式(如果需求在某种程度上有限且不会随之增长,则会呈二次方式) B)。

降低DP问题复杂性的一个常见技巧是获得最优解的值的上限O(nB)。通过这种方式,您可以修剪不必要的行,获得S的时间复杂度(嗯,偶数B = 2M是一个上限,但是非常天真)。

事实证明,在我们的案例中,M,其中best_assignment(i)是警卫的最大需求。 事实上,考虑函数i,它为您提供了通过第一个j警卫的最少金币数量。 让M成为需求best_assignment(j - 1) > M的守卫。如果j-1,那么显然整个序列的最佳分配是为最好的第一个best_assignment(j - 1) + M < 2M守卫分配保护并跳过其他守卫,否则上限由{{1}给出}。 但在第一种情况下可以best_assignment(j - 1)多少?它不能超过2M。 这可以通过矛盾来证明。我们假设best_assignment(j - 1) > 2M。在此作业中,警卫j-1是否已付款?不,因为2M - d_{j-1} > d_{j-1},因此无需支付。同样的论点适用于j-2j-3,...... 1,因此没有支付任何警卫,这是荒谬的,除非M = 0(一个非常天真的案例需要检查)

由于上限被证明是2M,上面所示的带有n列和2M行的DP解决了问题,时间复杂度O(nM)和空间复杂性O(M)

答案 1 :(得分:2)

function crossCost(amtPaidAlready, curIdx, demands){
    //base case: we are at the end of the line
    if (curIdx >= demands.size()){
        return amtPaidAlready;
    }

    costIfWePay = crossCost(amtPaidAlready + demands[curIdx], curIdx+1, demands);
    //can we skip paying the guard?
    if (demands[curIdx] < amtPaidAlready){
        costIfWeDontPay = crossCost(amtPaidAlready, curIdx+1, demands);
        return min(costIfWePay, costIfWeDontPay);
    }
    //can't skip paying
    else{
        return costIfWePay;
    }
}

这在O(2 ^ N)时间运行,因为它可能每次执行时调用自己两次。它是memoization的一个很好的候选者,因为它是一个没有副作用的pure function

答案 2 :(得分:0)

这是我的方法:

int guards[N];
int minSpent;

void func(int pos, int current_spent){
    if(pos > N)
        return;
    if(pos == N && current_spent < minSpent){
        minSpent = current_spent;
        return;
    }

    if(guards[pos] < current_spent)      // If current guard can be skipped
        func(pos+1,current_spent);       // just skip it to the next guard
    func(pos+1,current_spent+guards[pos]);   // In either cases try taking the current guard
}

以这种方式使用:

minSpent = MAX_NUM; 
func(1,guards[0]);

这将尝试所有可能的O(2 ^ N),希望这会有所帮助。