我如何使用DP解决这个问题?

时间:2015-09-15 05:40:44

标签: algorithm recursion matrix dynamic-programming

问题链接:http://codeforces.com/contest/2/problem/B

有一个方阵矩阵n×n,由非负整数组成。你应该找到这样的方式

从矩阵的左上角开始; 每个后续单元格位于当前单元格的右侧或下方; 这种方式在右下方的细胞中结束。 而且,如果我们将所有数字相乘,结果应该是最小的“圆形”。换句话说,它应该以尽可能少的零结束。

输入 第一行包含整数n(2≤n≤1000),n是矩阵的大小。然后跟随包含矩阵元素的n行(非负整数不超过10 ^ 9)。

输出 在第一行中打印最少数量的尾随零。在第二行打印相应的方式本身。

我想到了以下内容:最后,无论答案是什么,它都应该包含2和5的最小功率。因此,我所做的是,对于输入矩阵中的每个条目,我计算了2和5的幂并将它们存储在单独的矩阵中。

    for (i = 0; i < n; i++)
    {
        for ( j = 0; j < n; j++)
        {
            cin>>foo;
            matrix[i][j] = foo;
            int n1 = calctwo(foo);   // calculates the number of 2's in factorisation of that number
            int n2 = calcfive(foo); // calculates number of 5's
            two[i][j] = n1;
            five[i][j] = n2;
        }
    }

之后,我这样做了:

for (i = 0; i < n; i++)
{
    for ( j = 0; j < n; j++ )
    {
        dp[i][j] = min(two[i][j],five[i][j]);  // Here, dp[i][j] will store minimum number of 2's and 5's. 
    }
}

但上述并不是真正有效的答案,我不知道为什么?我实施了正确的方法吗?或者,这是解决这个问题的正确方法吗?

编辑:这是我计算一个数字中两个数和五个数的函数。

int calctwo (int foo)
{
    int counter = 0;
    while (foo%2 == 0)
    {
        if (foo%2 == 0)
        {
            counter++;
            foo = foo/2;
        }
        else
            break;
    }
    return counter;
}

int calcfive (int foo)
{
    int counter = 0;
    while (foo%5 == 0)
    {
        if (foo%5 == 0)
        {
            counter++;
            foo = foo/5;
        }
        else
            break;
    }
    return counter;
}

Edit2:链接中给出的I / O示例:

输入:

3
1 2 3
4 5 6
7 8 9

输出:

0
DDRR

4 个答案:

答案 0 :(得分:3)

由于您只对尾随零的数量感兴趣,因此您只需要考虑25的功能,您可以将它们保存在两个单独的nxn数组中。所以对于数组

1 2 3
4 5 6
7 8 9

你只需保留数组

the powers of 2    the powers of 5       
0 1 0              0 0 0
2 0 1              0 1 0
0 3 0              0 0 0

问题的见解如下。请注意,如果找到最小化2的幂的总和的路径和最小化5的幂次数之和的路径,则答案是具有这两个路径的较低值的路径。因此,您将问题减少到以下经典dp问题的两次应用:找到一条路径,从左上角开始到右下角结束,这样它的元素总和最小。再次,按照这个例子,我们有:

 minimal path for the 
 powers of 2                 value
 * * -                         2
 - * * 
 - - *

 minimal path for the 
 powers of 5                 value
 * - -                         0
 * - - 
 * * *

所以你的回答是

 * - -                      
 * - - 
 * * *

0

注1

看起来两条最优路径中的最小值只能给出一个上限,所以可能出现的问题是:这个界限是否实际达到了?答案是肯定的。为方便起见,让2的最佳路径中的2的数量为a,沿5的最佳路径的5的数量为{{1} }。不失一般性假设两条最优路径的最小值是2的幂(即b)的最小路径。让最小路径上的5的数量为a < b。现在的问题是:是否存在多达5个,因为沿着这条路径有2个(即c?)。假设答案是否定的。这意味着沿着最小路径(即c >= a)的2比5更少。由于5路径的最佳值为c < a,因此每5条路径至少有b 5个路径。对于最小路径,这也应该是正确的。这意味着b。我们有c > b所以c < a,但最初的假设是a > b。矛盾。

注2

您可能还需要考虑矩阵中存在元素a < b的情况。当产品为1时,我假设尾随零的数量。在这种情况下,如果算法产生的值大于1,则应输出1并打印通过元素{{1 }}

答案 1 :(得分:0)

这是代码。我已使用action = "http://localhost:9767/ProspectWorxService.svc/SaveUploadedFile?Id=" + globalId将因子2和5存储在矩阵中。

pair<int,int>

就我检查的测试用例而言,此代码可以得到很好的结果。如果您对此代码有任何疑问,请在评论中提问。

编辑: 基本的思维过程。
要到达目的地,只有2个选项。我从目的地开始,以避免路径提前计算的问题,因为如果2具有相同的最小值,那么我们选择其中任何一个。如果已计算到目的地的路径,则无关紧要。

最小的是检查哪一对更合适。如果一对具有最少2或5,则它将产生更少的0。

答案 2 :(得分:0)

这是一个使用Javascript和函数式编程的解决方案。 它依赖于几个功能:

  • 核心函数是smallest_trailer递归遍历网格。我已选择进入4个可能的方向,左边&#34; L&#34;,右边&#34; R&#34;,向下&#34; D&#34;和&#34; U&#34;。在同一个细胞上传递两次是不可能的。选择的方向是尾随零数最小的方向。尾随零的计数用于另一个函数。
  • 函数zero_trailer(p,n,nbz)假设您已到达具有值p的单元格,而您已经拥有累加器n并且在途中遇到nbz个零。该函数返回一个包含两个元素的数组,即新的零数和新的累加器。累加器的功效为2或5.该函数使用辅助函数pow_2_5(n),它在n内返回2和5的幂。
  • 其他函数更具有传闻性:deepCopy(arr)生成数组arr的标准深层副本,out_bound(i,j,n)如果单元格(i,j)超出网格范围则返回true大小为nmyMinIndex(arr)返回二维数组数组的最小索引(每个子数组包含尾随零的nb和作为字符串的路径)。 min只取决于子阵列的第一个元素。
  • MAX_SAFE_INTEGER是路径错误时最大尾随零数的(大)常量(例如,超出范围)。

以下是代码,该代码适用于上述注释和原始链接中给出的示例。

var MAX_SAFE_INTEGER = 9007199254740991;

function pow_2_5(n) {
  // returns the power of 2 and 5 inside n
  function pow_not_2_5(k) {
    if (k%2===0) {
      return pow_not_2_5(k/2);
    }
    else if (k%5===0) {
      return pow_not_2_5(k/5);
    }
    else {
      return k;
    }
  }
  return n/pow_not_2_5(n);
}

function zero_trailer(p,n,nbz) {
  // takes an input two numbers p and n that should be multiplied and a given initial number of zeros (nbz = nb of zeros)
  // n is the accumulator of previous multiplications (a power of 5 or 2)
  // returns an array [kbz, k] where kbz is the total new number of zeros (nbz + the trailing zeros from the multiplication of p and n)
  // and k is the new accumulator (typically a power of 5 or 2)
  function zero_aux(k,kbz) {
    if (k===0) {
      return [1,0];
    }
    else if (k%10===0) {
      return zero_aux(k/10,kbz+1);
    }
    else {
      return [kbz,k];
    }
  }
  return zero_aux(pow_2_5(p)*n,nbz);  
}

function out_bound(i,j,n) {
  return !((i>=0)&&(i<n)&&(j>=0)&&(j<n));
}

function deepCopy(arr){
  var toR = new Array(arr.length);
  for(var i=0;i<arr.length;i++){
    var toRi = new Array(arr[i].length);
    for(var j=0;j<arr[i].length;j++){
      toRi[j] = arr[i][j];
    }
    toR[i] = toRi;
  }
  return toR;
}

function myMinIndex(arr) {
  var min = arr[0][0];
  var minIndex = 0;
  for (var i = 1; i < arr.length; i++) {
      if (arr[i][0] < min) {
          minIndex = i;
          min = arr[i][0];
      }
  }
  return minIndex;
}

function smallest_trailer(grid) {
  var n = grid.length;
  function st_aux(i,j,grid_aux, acc_mult, nb_z, path) {

    if ((i===n-1)&&(j===n-1)) {          
      var tmp_acc_nbz_f = zero_trailer(grid_aux[i][j],acc_mult,nb_z);
      return [tmp_acc_nbz_f[0], path];
    }
    else if (out_bound(i,j,n)) {
      return [MAX_SAFE_INTEGER,[]];
    }
    else if (grid_aux[i][j]<0) {
      return [MAX_SAFE_INTEGER,[]];
    }
    else {
      var tmp_acc_nbz = zero_trailer(grid_aux[i][j],acc_mult,nb_z) ;
      grid_aux[i][j]=-1;
      var res = [st_aux(i+1,j,deepCopy(grid_aux), tmp_acc_nbz[1], tmp_acc_nbz[0], path+"D"), 
                 st_aux(i-1,j,deepCopy(grid_aux), tmp_acc_nbz[1], tmp_acc_nbz[0], path+"U"),
                 st_aux(i,j+1,deepCopy(grid_aux), tmp_acc_nbz[1], tmp_acc_nbz[0], path+"R"),
                 st_aux(i,j-1,deepCopy(grid_aux), tmp_acc_nbz[1], tmp_acc_nbz[0], path+"L")];
      return res[myMinIndex(res)];      
    }
  }
  return st_aux(0,0,grid, 1, 0, "");
}

myGrid = [[1, 25, 100],[2, 1, 25],[100, 5, 1]];
console.log(smallest_trailer(myGrid)); //[0,"RDDR"]
myGrid = [[1, 2, 100],[25, 1, 5],[100, 25, 1]];
console.log(smallest_trailer(myGrid)); //[0,"DRDR"]
myGrid = [[1, 10, 1, 1, 1],[1, 1, 1, 10, 1],[10, 10, 10, 10, 1],[10, 10, 10, 10, 1],[10, 10, 10, 10, 1]];
console.log(smallest_trailer(myGrid)); //[0,"DRRURRDDDD"]

答案 3 :(得分:0)

这是我的动态编程解决方案。

https://app.codility.com/demo/results/trainingAXFQ5B-SZQ/

为了更好地理解,我们可以简化任务并假设矩阵中不存在零(即矩阵仅包含正整数),则Java解决方案如下:

class Solution {
    public int solution(int[][] a) {
        int minPws[][] = new int[a.length][a[0].length];

        int minPws2 = getMinPws(a, minPws, 2);
        int minPws5 = getMinPws(a, minPws, 5);
        return min(minPws2, minPws5);
    }

    private int getMinPws(int[][] a, int[][] minPws, int p) {
        minPws[0][0] = pws(a[0][0], p);
        //Fullfill the first row
        for (int j = 1; j < a[0].length; j++) {
            minPws[0][j] = minPws[0][j-1] + pws(a[0][j], p);
        }
        //Fullfill the first column
        for (int i = 1; i < a.length; i++) {
            minPws[i][0] = minPws[i-1][0] + pws(a[i][0], p);
        }
        //Fullfill the rest of matrix
        for (int i = 1; i < a.length; i++) {
            for (int j = 1; j < a[0].length; j++) {
                minPws[i][j] = min(minPws[i-1][j], minPws[i][j-1]) + pws(a[i][j], p);
            }
        }

        return minPws[a.length-1][a[0].length-1];
    }

    private int pws(int n, int p) {
        //Only when n > 0
        int pws = 0;
        while (n % p == 0) {
            pws++;
            n /= p;
        }
        return pws;
    }

    private int min(int a, int b) {
        return (a < b) ? a : b;
    }
}