如何设计动态编程算法如下

时间:2014-03-28 03:11:43

标签: algorithm dynamic-programming

我们有n个邮票,其中每个邮票我有一个特定面额d(i)和尺寸s(i)。所有面额都是不同的,我们可以多次使用邮票面额。现在我想设计一个算法,给定d(i)和s(i)的邮票和邮资金额p,找到其面额将恰好加到p的最小邮票总数?

我知道这是一个动态编程问题,而且我觉得它应该像背包问题一样被解决。但我完全感到困惑,因为这里邮票的最小总尺寸应该加到p。我想出了下面的重复,我知道这不会是真的,因为它不会检查总的最小尺寸是否合计为p:

M(p)=min{M(p-d(i))}+s(i),M(p-d(i))} for i from 1 to n

另外我不知道如何制表(为了编写动态编程的迭代版本)。

我的猜测是我必须有一个尺寸为p和d(i)的二维数组,每个单元格都用s(i)填充。

2 个答案:

答案 0 :(得分:2)

你的猜测是正确的。这是二维DP问题。但在我们宣布我们需要达到递推公式之前,我们会看到该公式中有多少个字段在变化。

在您的问题陈述中有两件事:1)邮票大小需要最小化。 2)所有邮票应加起来为P.如果你是初学者,请不要认为DP会立刻自下而上。首先考虑自上而下的递归方法,这应该在一些练习后变得容易思考。在这种情况下,让我们说你知道N个邮票的解决方案。让我们将这个解决方案表示为M(d(N),P),它表示M是N个标记中的解,其总和为P.要获得递归关系,请考虑如果最后一个标记(第N个)不是结果的一部分那么问题将减少为从N-1邮票中找到P.如果存在最后一个元素(第N个标记),则问题是从N-1个标记中找出P-d(N)和。其反复出现的关系如下:

M(N, P) = Min{ M(N-1, P), M(N-1, P - d(N))}

或更一般意义上说:

M(i, P) = Min{ M(i - 1, P), M(i - 1, P - d(i))}

正如您所看到的,这个递归公式中的两个字段不同,因此您必须考虑二维DP。

取两个轴,在X轴上取0到P全部和,在y轴取0到N(元素个数)。 迭代函数应如下所示。

set all M(0, j) and M(i, 0) = 0 for all i [0, N] and j [0, P]

for: i = 0 to N
   for: j = 0 to P
      for: int k = 0 to j
        if: j - P(k) >= 0 and M(i, j) < M(i-1, j-P(k))
           M(i, j) = M(i-1, j-P(k));


return M(N, P);

注意:我没有提到邮票的大小,因为很明显M中的字段将是那些需要最小化的选定邮票的大小。

答案 1 :(得分:1)

以下是DP复发关系,其中包含两个要求。首先你的问题类似于背包问题,只需要完全填充背包所需的更改,此处容量为p,(s(i),d(i))为项目类型。

DP解决方案: -

考虑n个项目(s(i),d(i))和邮资金额pValid(n,p)表示子问题的解决方案恰好合计为p: -

DP(n,p) = 0;
Valid(n,p) = 0;

if(Valid(n-1,p)) {

    DP(n,p) = DP(n-1,p);
    Valid(n,p) = true;

} 

if(Valid(n,p-d(n)))  {

  DP(n,p) = min{DP(n,p),DP(n,p-d(n)) + s(n)};
  Valid(n,p) = true;

}

最终结果:

if(Valid(n,p)) {

   return(DP(n,p));

}

else printf("no solution");

使用自下而上的DP解决方案: -

Valid[n][p+1] = {false};
DP[n][p+1] = {0};
Valid[0][0] = true;
DP[0][0] = 0;
for(int i=0;i<=p;i++) {

   if(s[0]<=i) {
       if(Valid[0][i-d[0]]) {

        DP[0][i] = s[0] + DP[0][i-d[0]];
        if(s[0]==i)
        Valid[0][i] = true;
       }          
   }
}

for(int j=0;j<n;j++)
    Valid[j][0] = true;

for(int j=1;j<n;j++) {

 for(int k=1;k<=p;k++) {

  if(Valid[j-1][k]) {

        DP[j][k] = DP[j-1][k];
        Valid[j][k] = true;

    } 

    if(Valid[j][k-d(j)])  {

      DP[j][k] = min{DP[j][k],DP[j][k-d(j)] + s[k]};
      Valid[j][k] = true;

    } 

 }

}