使用动态编程的餐厅最大利润

时间:2016-02-27 17:48:38

标签: java algorithm recursion dynamic-programming

这是一项任务,我花了两天的时间来提出一个解决方案,但仍然有很多困惑,但是在这里我需要清楚几点。以下是问题:

Yuckdonald's正考虑在QVH开设一系列餐厅。 n个可能的位置沿着一条直线,这些位置距QVH起点的距离以英里为单位,并以递增的顺序 m1,m2,....,mn 。约束如下:
1.在每个地点,Yuckdonald可以开设一家餐厅,预计在 i 开设餐厅的利润为 pi
2.任何两家餐馆应至少相隔 k 英里,其中 k 是正整数

我的解决方案:

public class RestaurantProblem {

    int[] Profit;
    int[] P;
    int[] L;
    int k;



    public RestaurantProblem(int[] L , int[] P, int k) {
        this.L = L;
        this.P = P;
        this.k = k;
        Profit = new int[L.length];
    }



    public int compute(int i){
        if(i==0)
            return 0;


        Profit[i]= P[i]+(L[i]-L[i-1]< k ? 0:compute(i-1));//if condition satisfies then adding previous otherwise zero
        if (Profit[i]<compute(i-1)){
                Profit[i] = compute(i-1);
            }
        return Profit[i];
    }

    public static void main(String args[]){
        int[] m = {0,5,10,15,19,25,28,29};
        int[] p = {0,10,4,61,21,13,19,15};
        int k = 5;

        RestaurantProblem rp = new RestaurantProblem(m, p ,k);
        rp.compute(m.length-1);
        for(int n : rp.Profit)
            System.out.println(n);

    }

}

这个解决方案给了我88但是如果我排除(餐厅25岁,利润13)并包括(餐厅28,利润19)我最多可以有94个..

如果我错了,请指出我,或者如果真的如此,我怎么能实现这一点。

2 个答案:

答案 0 :(得分:2)

我能够发现2个错误:

实际使用动态编程

,您只是将结果存储在数据结构中,如果程序以您编写的方式工作并且您只进行了1次递归调用,那么这对性能来说就不会那么糟糕。

但是,您执行至少2次递归调用。因此,该计划以Ω(2^n)而不是O(n)运行。

动态编程通常像这样(伪代码):

calculate(input) {
     if (value already calculated for input)
          return previously calculated value
     else
         calculate and store value for input and return result
}

您可以通过将数组元素初始化为-1(或0,如果所有利润均为正数)来执行此操作:

Profit = new int[L.length];
Arrays.fill(Profit, -1); // no need to do this, if you are using 0
public int compute(int i) {
    if (Profit[i] >= 0) { // modify the check, if you're using 0 for non-calculated values
        // reuse already calculated value
        return Profit[i];
    }
    ...

您认为之前的餐厅只能建在以前的位置

Profit[i] = P[i] + (L[i]-L[i-1]< k ? 0 : compute(i-1));
                                     ^
                  Just ignores all positions before i-1

相反,您应该将利润用于至少k英里之外的最后一个位置。

示例

k = 3
L   1   2   3   ...   100
P   5   5   5   ...     5

此处L[i] - L[i-1] < k适用于所有i,因此结果只有P[99] = 5,但应为34 * 5 = 170

int[] lastPos;

public RestaurantProblem(int[] L, int[] P, int k) {
    this.L = L;
    this.P = P;
    this.k = k;
    Profit = new int[L.length];
    lastPos = new int[L.length];
    Arrays.fill(lastPos, -2);
    Arrays.fill(Profit, -1);
}

public int computeLastPos(int i) {
    if (i < 0) {
        return -1;
    }
    if (lastPos[i] >= -1) {
        return lastPos[i];
    }
    int max = L[i] - k;
    int lastLastPos = computeLastPos(i - 1), temp;
    while ((temp = lastLastPos + 1) < i && L[temp] <= max) {
        lastLastPos++;
    }
    return lastPos[i] = lastLastPos;
}

public int compute(int i) {
    if (i < 0) {
        // no restaurants can be build before pos 0
        return 0;
    }
    if (Profit[i] >= 0) { // modify the check, if you're using 0 for non-calculated values
        // reuse already calculated value
        return Profit[i];
    }

    int profitNoRestaurant = compute(i - 1);
    if (P[i] <= 0) {
        // no profit can be gained by building this restaurant
        return Profit[i] = profitNoRestaurant;
    }

    return Profit[i] = Math.max(profitNoRestaurant, P[i] + compute(computeLastPos(i)));
}

答案 1 :(得分:0)

据我所知,问题可以用二维状态空间建模,我在目前的实现中找不到。对于每个(i,j) in {0,...,n-1} times {0,...,n-1}`让

profit(i,j) := the maximum profit attainable for selecting locations
               from {0,...,i} where the farthest location selected is
               no further than at position j
               (or minus infinity if no such solution exist)

并注意重现关系

profit(i,j) = min{ p[i] + profit(i-1,lastpos(i)),
                          profit(i-1,j)
                 }

其中lastpos(i)是距离起点最远的位置,但距离k不到i位置;上面的第一种情况对应于选择位置i到解决方案中,而第二种情况对应于在解决方案中省略位置j。通过评估profit(n-1,n-1)可以获得整体解决方案;评估可以递归方式完成,也可以自下而上的方式填充二维数组,并在(n-1,n-1)返回其内容。