【问题标题】:Maximum sum path in a matrix from top to bottom矩阵中从上到下的最大和路径
【发布时间】:2020-11-07 00:47:23
【问题描述】:

考虑一个 n*m 矩阵。假设矩阵中的每个单元格都分配了一个值。我们可以从矩阵第一行的每个单元格开始。允许的移动是对角向左、向下或对角向右,即从位置(i, j) 下一步移动可以是(i+1, j),或(i+1, j+1),或(i+1, j-1)。 (当然如果索引不在数组的范围之外)

添加一个额外的限制:只允许通过(至少一次)所有列的路径。

找到满足允许移动的元素的最大总和。 例如矩阵:

1 15 2   
9 7 5
9 2 4   
6 9 -1

总和是相等的:

28

因为路径是15+5+2+6=28

主要特点是我需要使用动态方法。对于我可以做的所有列没有限制的任务:

            var matrix = new int[,]{ { 1, 15, 2 }, //start matrix
                                     { 9, 7, 5 },
                                     { 9, 2, 4},
                                     { 6, 9, -1 } };
            long n = matrix.GetLength(0);
            long m = matrix.GetLength(1);
            
            var sum = new List<long[]>(); // list [n][m] of maxsums 
            for (int i = 0; i < n; i++)
            {
                sum.Add(new long[m].Select(e => e = long.MinValue).ToArray());
            }
            for (int i = 0; i < m; i++)
            {
                sum[0][i] = matrix[0, i]; //sums at first line equal first line in matrix
            }

            for (int i = 0; i < n - 1; i++)
            {
                for (int j = 0; j < m; j++)
                {
                    if (j > 0) sum[i + 1][j - 1] = Math.Max(sum[i][j] + matrix[i + 1, j - 1], sum[i + 1][j - 1]); // diagonally left
                    sum[i + 1][j] = Math.Max(sum[i][j] + matrix[i + 1, j], sum[i + 1][j]); // downwards
                    if (j < m - 1) sum[i + 1][j + 1] = Math.Max(sum[i][j] + matrix[i + 1, j + 1], sum[i + 1][j + 1]); //diagonally right
                }
            }
           
            long max = sum[(int)n - 1].Max(); //maximum sum among all paths (answer)

对于同一个矩阵,最大和将等于:

42

因为路径是15+9+9+9=42

现在我可以计算所有路径的动态矩阵和有限制的总和吗?

【问题讨论】:

  • 又是九月了吗?
  • 我不知道 28 的结果是怎样的。你能显示你要求和的数字吗
  • 我认为路径是15+5+2+6=28
  • 不需要从第一行开始吗?
  • 我需要。起始线是 - 1 15 2

标签: c# matrix dynamic-programming


【解决方案1】:

一个简单的方法是使用队列。

  1. 添加第一行
  2. 为队列中的每个项目添加任何其他有效移动
  3. 完成后,检查它是否是有效的组合
  4. 检查最后的最高值

它使用一个迭代器方法、Linq 和队列来返回当前的最佳结果。我建议你做的是研究涉及的部分,逐步检查并检查变量,使用Console.WriteLine 来查看正在发生的事情。如果您真的被卡住了,您可以随时询问有关此代码及其作用的更多问题。

队列的想法是,我们将第一行中的每个元素作为初始项添加到队列中(这是您给定规则的前提条件),然后我们查看队列中的第一个元素,然后从那个位置 (x,y) 我们遍历下一行中我们可以合法访问的所有下一个位置。队列还包含访问的列列表和该位置的值。它可以做不同的事情。也就是说,我们真的只需要知道所有访问过的元素的总和以及列列表等,这样我们就可以在之后验证路径。

注意:这不是最佳解决方案,它可以通过许多其他方式(更优雅)更有效地完成,并且可以使用更少的代码。但是涉及到很多常见的概念,值得理解

给定

private static Random _rand = new Random();

// this could be done with linq, however it's easy to see how it works
private static bool IsPathValid(int length, List<int> path)
{
    for (var i = 0; i < length; i++)
        if (!path.Contains(i))
            return false;
    return true;
}

迭代器

public static IEnumerable<IEnumerable<(int col, int value)>> FindPath(int[, ] matrix)
{
    var queue = new Queue<(int x, int y, List<(int col, int value)> path)>();
    // add the first row to the queue
    for (var i = 0; i < matrix.GetLength(1); i++)
        queue.Enqueue((i, 0, new List<(int col, int value)>()));
    // lets keep the higest found
    var highest = int.MinValue;
    // loop all queue items until none left
    while (queue.Any())
    {
        // get the next item out of the queue
        var(x, y, path) = queue.Dequeue();
        // add the path we are visiting
        path.Add((x, matrix[y, x]));
        // if we have looked at all the rows, then time to return
        if (y + 1 == matrix.GetLength(0))
        {
            // get a list of columns visited 
            var cols = path.Select(x => x.col).ToList();
            // check to see if all columns are visited
            if (IsPathValid(matrix.GetLength(1), cols))
            {
                var sum = path.Sum(x => x.value);
                // sum the path, if it's not the highest we don't care
                if (sum > highest)
                {
                    // we are the highest path so far so let's return it
                    yield return path;
                    highest = sum;
                }
            }

            continue;
        }

        // where ever we are, lets look at all the valid x's in the next row
        var start = Math.Max(0, x - 1);
        var finish = Math.Min(matrix.GetLength(1) - 1, x + 1);
        // add them to the queue
        // we inefficiently create a new path, as list is a reference type and we don't want to reuse it
        for (var newX = start; newX <= finish; newX++)
            queue.Enqueue((newX, y + 1, new List<(int col, int value)>(path)));
    }
}

用法

// create a random matrix, make sure there the dimensions are going to produce a result
var y = _rand.Next(2, 5);
var matrix = new int[_rand.Next(y, y + 3), y];

// fill and print the matrix
Console.WriteLine("Matrix");
Console.WriteLine();
for (var i = 0; i < matrix.GetLength(0); i++)
{
   for (var j = 0; j < matrix.GetLength(1); j++)
   {
      matrix[i, j] = _rand.Next(0, 20);
      Console.Write(matrix[i, j].ToString().PadLeft(3));
   }

   Console.WriteLine();
}


Console.WriteLine();
Console.WriteLine("Best Path (column,Value)");
Console.WriteLine();

// get the best, which be the last
var best = FindPath(matrix).Last();
foreach (var item in best)
{
   Console.WriteLine(item);
}

// show the result
Console.WriteLine();
Console.WriteLine("= " + best.Sum(x => x.value));

输出

Matrix

 14  9 17  0
 19  5 11 10
 17 12  9 13
  3 11  2  5
  0  0 12 15

Best Path (column,Value)

(0, 14)
(0, 19)
(1, 12)
(2, 2)
(3, 15)

= 62

Full Demo Here


其他资源

【讨论】:

  • 我添加了更多说明,不幸的是,您的方法需要太多内存来进行我的测试。
  • @kate,我可以创建一个更高效的算法,使用最少的内存可以快速处理处理
  • 我真的很想了解如何仅使用动态编程来实现解决方案,就像我之前解决更普遍的问题一样
  • @Kate 请定义动态规划。
  • @Kate 也就是说,您可以修复内存或修复处理。我可以给你一个处理效率最高(最快)的解决方案,但它需要更多内存,或者我可以给你一个节省内存但需要更多处理的解决方案
猜你喜欢
  • 2018-04-29
  • 1970-01-01
  • 1970-01-01
  • 2021-09-29
  • 1970-01-01
  • 2015-10-25
  • 1970-01-01
  • 1970-01-01
  • 2017-09-03
相关资源
最近更新 更多