动态编程 - topcoder

时间:2013-02-07 06:24:15

标签: dynamic-programming

我一直在尝试使用Topcoder上的dp教程。实践中遇到的一个问题是MiniPaint。我想我已经得到了部分解决方案 - 找到最低限度的没有。对于给定的没有错误的错误。中风,每行然后计算整个图片(再次使用dp,类似于背包问题)。但是,我不知道如何计算分钟。每一行都没有。

P.S我后来找到了match editorial,但找到了min的代码。没有。每一行的错误画面似乎都是错误的。有人可以解释他们在代码中做了什么吗?

2 个答案:

答案 0 :(得分:0)

stripScore()函数返回每行的错误描绘次数,给出描边量可用于绘制它。虽然我不确定rowid参数是否正确,但我们的想法是从特定行的start开始,可以使用needed个笔画数量以及该区域的颜色就在它之前。

  

该算法的关键在于,第k个区域右侧区域的最佳得分由所需的笔划数和用于绘制第(k-1)个区域的颜色唯一确定。

答案 1 :(得分:0)

直觉

我已经连续三天对这个问题b不休,没有意识到它需要连续两次使用动态编程逻辑。与topcoder可用的方法相比,我的方法是自下而上的。 首先,我将计算我可以用maxStrokes笔画绘制的单元格的最大数量,而不是计算我可以达到的最小的错误绘画数量。通过从矩阵的总单元格中减去我的发现,可以轻松地计算出结果。但是我该怎么做呢?最初的观察必须是这样的事实,即每一行都可以为我提供一些绘制的单元格,以换取许多笔触。 这不依赖于其余的行。这意味着,对于每一行,我都可以计算出在特定行上具有一定笔划数的最大单元数。

示例

输入 = ['BBWWB','WBWWW'], maxStrokes = 3
现在让我们来看第一行BBWWB,并将C表示为我可以用Q笔划绘制的最大单元数
Q C
0 0(我不能用0笔画画)
1 3( BB WW B
2 4( BBWW B)
3 5( BBWWB
我们可以用长度为4的数组轻松表示上述结果,该数组为每个索引(笔划)存储可以绘制的最大单元数,即 [0,3,4,5]
可以很容易地看到第二行以相同的方式具有数组 [0,4,4,5]

现在,仅通过这两个数组就可以轻松计算出结果,因为我们正在寻找的是两个选择的组合,每个计算出的数组一个,这将为我提供可以绘制的最大数量的单元格3招。我有什么选择呢?我数组中的每一项都代表我可以用索引笔触绘制的最大单元数。因此,对于第一个数组,一个选择是用2个笔触绘制4个单元。 然后,我可以将该选择与第二个数组的第一项4结合起来,这意味着我可以用1笔画绘制4个单元格。我的最终结果将是2 + = 3笔画的4 + 4 = 8个单元格,这恰好是我能得到的最好结果。然后,输出将平凡地为2 * 5-8 = 2最小误涂。但是,我们需要找到一种一种最佳方法来计算每一行中项目的不同组合,以及它们可以产生多少总和。

过程

算法的第一部分填充了两个非常重要的表。让我们用 N,M 表示我得到的矩阵的尺寸。第一个表dp N * M * maxStrokes 矩阵。 dp[i][j][k]表示从第0个单元到第i行第k个笔划,我可以绘制的最大单元数。至于maxPainted表,这是一个N * maxStrokes矩阵。 maxPainted[i][k]存储我在第i行中用k笔画可以绘制的最大单元数,并且与上面示例中计算出的数组相同。为了计算后者,我需要首先计算dp。公式如下:

dp[i][j][k]= MAX (1,dp[i][r][k]+1 (if A[i][j]==A[i][r]) ,dp[i][r][k-1]+1 (if A[i][j]!=A[i][r])),每0 <= r
可以解释为:我最多可以用k笔画绘制到第i行的第j个单元格的单元格数量是:

  • 1,因为我可以忽略所有先前的单元格,并单独绘制该单元格
  • dp[i][r][k]+1,因为当使用A[i][j]==A[i][r]时,我可以在不增加笔触的情况下扩展该颜色
  • dp[i][r][k-1]+1,因为在A[i][j]==A[i][r]时,我必须使用新笔触来绘制A[i][j]

现在很明显,需要计算dp表以获取每一行的最佳可能方案,即我可以在所有可能的笔划数下绘制的最大单元数。但是,一旦计算出maxPainted表以得到结果,该如何利用它?

我方法的第二部分使用了 0-1背包问题的一种变体,以便计算可用maxStrokes的笔触可以绘制的最大单元数。真正使这一挑战成为现实的是,与经典的背包相比,我只允许从每一行中选择一项,然后计算所有可能的组合,这些组合不超过所需的笔划约束。为此,我将首先创建一个长度为 N * M +1 的新数组,称为possSums。让我们用possSums[S]表示达到总和 S 所需的 MINIMUM 个笔划数。我的目标是计算每一行对此数组的贡献。让我们通过前面的示例进行演示。

所以我有2 * 5的输入,因此possSums数组将由10 + 1个元素组成,我们将其设置为 Infinity ,因为我们正尝试最大程度地减少所需的击键次数达到上述金额。
因此,possSums = [0,∞,∞,∞,∞,∞,∞,∞,∞,∞,∞] ,因为我可以画0,所以第一项是0笔触为0的单元格。我们现在要做的是计算每一行对possSums的贡献。这意味着对于我的maxPainted数组的每一行,每个元素都需要提供一个特定的总和,这将模拟所选的总和。正如我们先前所演示的,maxPainted[0] = [0,3,4,5] 。该行的贡献必须允许0、3、4和5作为我的possSums数组中分别可使用笔划0、1、2、3的可实现总和。 possSum然后将被转换为possSums = [0,∞,∞,1,2,3,∞,∞,∞,∞,∞] 。下一行是maxPainted[1] = [0,4,4,5] ,现在必须再次更改possSum,以允许通过选择每个项目来进行组合。请注意,每个更改都必须与同一行中的其他更改不相关。例如,如果我们首先允许通过选择maxPainted[1]的第一项来发生 sum = 4 ,则不能通过进一步选择 sum = 9 来允许同一数组的3d项,本质上意味着不能考虑同一行中项的组合。为了确保不考虑这种情况,我为每一行创建了possSums数组的副本,我将对其进行必要的修改,而不是原始数组。考虑了maxPainted[1]中的所有项目之后,possSums看起来像这样possSums = [0,∞,∞,1,1,3,∞,2,3,4,6] ,在第8个索引(总和= 8)上,我最多可以绘制3个笔触来绘制单元格。因此我的输出将是2 * 5-8 = 2

var minipaint=(A,maxStrokes)=>{
    let n=A.length,m=A[0].length
    , maxPainted=[...Array(n)].map(d=>[...Array(maxStrokes+1)].map(d=>0))
    , dp=[...Array(n)].map(d=>[...Array(m)].map(d=>[...Array(maxStrokes+1)].map(d=>0)))
    for (let k = 1; k <=maxStrokes; k++) 
        for (let i = 0; i <n; i++) 
            for (let j = 0; j <m; j++) {               
                    dp[i][j][k]=1 //i can always just paint the damn thing alone
                    //for every previous cell of this row
                    //consider painting it and then painting my current cell j
                    for (let p = 0; p <j; p++)
                        if(A[i][p]===A[i][j]) //if the cells are the same, i dont need to use an extra stroke
                            dp[i][j][k]=Math.max(dp[i][p][k]+1,dp[i][j][k])                    
                        else//however if they are,im using an extra stroke( going from k-1 to k)
                            dp[i][j][k]=Math.max(dp[i][p][k-1]+1,dp[i][j][k])                    
                                      
                    maxPainted[i][k]=Math.max(maxPainted[i][k],dp[i][j][k])//store the maximum cells I can paint with k strokes
            }             
                
    //this is where the knapsack VARIANT happens: 
    // Essentially I want to maximize the sum of my selection of strokes 
    // For each row, I can pick maximum of 1 item. Thing is,I have a constraint of my total
    // strokes used, so I will create an array of possSums whose index represent the sum I wanna reach, and values represent the MINIMUM strokes needed to reach that very sum. 

    // so possSums[k]=min Number of strokes needed to reach sum K 
    let result=0,possSums=[...Array(n*m+1)].map(d=>Infinity)
    
    //basecase, I can paint 0 cells with 0 strokes
    possSums[0]=0
    for (let i = 0; i < n; i++) {
        let curr=maxPainted[i],
            temp=[...possSums]// I create a clone of my possSums,
            // where for each row, I intend to alter It instead of the original array
            // in order to avoid cases where two items from the same row contribute to 
            // the same sum, which of course is incorrect.

        for (let stroke = 0; stroke <=maxStrokes; stroke++) {
            let maxCells=curr[stroke]           
            //so the way this happens is : 
            for (let sum = 0; sum <=n*m-maxCells; sum++) {
                let oldWeight=possSums[sum]//consider if UP until now, the sum was possible
                if(oldWeight==Infinity)// if it wasnt possible, i cant extend it with my maxCells
                    continue;
                // <GAME CHANGER THAT ALLOWS 1 PICK PER ROW 
                let minWeight=temp[sum+maxCells]//now, consider extending it by sum+maxCells

                // ALTERING THE TEMP ARRAY INSTEAD SO MY POTENTIAL RESULTS ARE NOT AFFECTED BY THE 
                // SUMS THAT WERE ALLOWED DURING THE SAME ROW 
                temp[sum+maxCells]=Math.min(minWeight,oldWeight+stroke)
                
                if(temp[sum+maxCells]<=maxStrokes)
                    result=Math.max(result,sum+maxCells)    
            }
        }
        possSums=temp
    }
    return n*m-result // returning the total number of cells minus the maximum I can paint with maxStrokes
}