【问题标题】:how to use recursion to formulate using simple array如何使用递归来使用简单数组进行公式化
【发布时间】:2016-02-23 10:46:01
【问题描述】:

我是编码初学者。我想使用简单的递归和数组来解决以下问题。但我无法想象它。我想出了使用链接列表的解决方案。以下是问题和我的解决方法

给定 n 行整数,使得第 i 行 (1 <= i <= n) 包含 i 个整数。使用以下路径规则集,找到 具有最大权重的路径。

路径遍历规则:

  1. 有效的路径序列是自上而下的,即从第一行的整数开始,遍历所有行,只选择一个 每行中的整数。

  2. 从第 i 行中的任何第 j 个整数,即row[i][j],遍历可以发生向下(即到row[i+1][j])或斜向下 向右(即row[i+1][j+1])。

Path 的权重是 Path 中整数值的总和 顺序。

例子:

    No. of Rows: 5 
        4 
        2    9 
        15   1    3 
        16   92  41  44 
        8   142  6    4    8 

预期输出:4、2、15、92、142(最大重量为 255)

Sol.c

#include<stdio.h>
#include<stdlib.h>

int n,**ar;

struct n
{
 int i,j;
 int w;
 struct n *ptr;
};

struct n* maxweight(int i,int j,struct n* x)
{
 struct n* tmp=malloc(sizeof(struct n)),*t1,*t2;
 tmp->i=i;
 tmp->j=j;
 tmp->ptr=x;
 tmp->w=ar[i][j];
 if(x)tmp->w+=x->w;
 if(i==n-1)return tmp;
 t1=maxweight(i+1,j,tmp);
 t2=maxweight(i+1,j+1,tmp);
 if(t1->w>t2->w)return t1;
 return t2;
}

int main()
{
 int i,j;
 struct n * s;
 printf("Enter the value of n\n");
 scanf("%d",&n);
 ar=malloc(n*sizeof(int*));
 for(i=0;i<n;i++)
 {
  ar[i]=malloc((i+1)*sizeof(int));
  for(j=0;j<=i;j++)scanf("%d",&ar[i][j]);
 }
 s=maxweight(0,0,NULL);
 printf("MAX WEIGHT is :%d\nPATH: ",s->w);
 while(s)
 {
  printf("%d ",ar[s->i][s->j]);
  s=s->ptr;
 }
 printf("\n");
 return 0;
}

如何在不使用 n x n 矩阵的链接列表的情况下简单地使用递归来解决这个问题?动态规划是否适用于这个问题。

【问题讨论】:

  • 查找 DFS 和/或 BFS 算法。和一般的图搜索。
  • 递归思考的关键是递归思考。
  • @Ike 在所有这些递归笑话中都有一个共同的问题。缺乏基本情况。
  • 我认为它们会导致堆栈溢出。 :-D
  • @EugeneSh.: 你是说if (joke == old) return 0;?这迟早会自动发生。

标签: c recursion dynamic-programming backtracking


【解决方案1】:

专注于计算前方的路径的权重; 不要回头。

首先解决一个微不足道的边缘情况。假设你到了最后一行。那么就没有更多的事情可循了;剩余路径的权重为零。

在代码中:

int getWeight(int i, int j)
{
    int remaining = 0;

在任何其他行中,您必须做出选择。你应该向左还是向右?由于目前无法知道哪个最好,您只需要尝试两个方向:

    if (i < lastRow)
    {
        int weightLeft  = getWeight(i + 1, j);
        int weightRight = getWeight(i + 1, j + 1);

注意我递归地调用了我自己的函数; 盲目相信该函数能够为剩余路径得出最佳权重!

尝试了两个方向后,选择重量最高的:

        int best_j = weightLeft > weightRight ? j : j + 1;

现在我们再走一次选择的路径。

        remaining = getWeight(i + 1, best_j);
    }

这不是很有效,但它有助于收集最佳路径的各个步骤。我将使用一个简单的数组pathColumns

    pathColumns[i] = j;

最后,我们需要对这些值求和。

    return row[i][j] + remaining;
}

要让整个事物运动起来,只需调用该函数,并将顶部单元格的坐标传递给它。出于实际原因,我将所有数组设为 base-0。所以顶部单元格是row[0][0]

printf("Optimal weight: %d\n", getWeight(0, 0));

把它们放在一起:

#include <stdio.h>

#define n 5

int pathColumns[n] = {0};

int row[n][n] =
{
    {4},
    {2, 9},
    {15, 1, 3},
    {16, 92, 41, 44},
    {8, 142, 6, 4, 8}
};

int getWeight(int i, int j)
{
    int remaining = 0;
    if (i < n-1)    /* with base-0, the last row is n-1 */
    {
        int weightLeft  = getWeight(i + 1, j);
        int weightRight = getWeight(i + 1, j + 1);
        int best_j = weightLeft > weightRight ? j : j + 1;
        remaining = getWeight(i + 1, best_j);
    }
    pathColumns[i] = j;
    return row[i][j] + remaining;
}

int main()
{
    int i;
    printf("Optimal weight: %d\n", getWeight(0, 0));
    for (i = 0; i < n; i++)
    {
        int j = pathColumns[i];
        printf("(%d, %d) = %d\n", i+1, j+1, row[i][j]);
        /* NOTE: +1 is a correction to bring the output back to base-1 */
    }
    return 0;
}

输出:

Optimal weight: 255
(1, 1) = 4
(2, 1) = 2
(3, 1) = 15
(4, 2) = 92
(5, 2) = 142

工作原理

我们希望getWeight(0, 0) 返回此金字塔的最重路径。

          4  <---- (0, 0) is our starting point
         / \
        2   9
      /  \ /  \
    15    1    3
    / \  / \  / \
  16   92   41   44
 /  \ /  \ /  \ /  \
8   142   6    4    8

递归算法进行两次递归调用。

  • getWeight(1, 0) 必须得到我们起点下方和左侧的子金字塔的最重路径。
  • getWeight(1, 1) 必须得到我们起点下方和右侧的子金字塔的最重路径。

两个子金字塔:

        2  <--- (1, 0)         9  <--- (1, 1)
      /  \                    /  \
    15    1                  1    3
    / \  / \                / \  / \
  16   92   41            92   41   44
 /  \ /  \ /  \          /  \ /  \ /  \
8   142   6    4      142    6    4    8

假设 getWeight(1, 0)getWeight(1, 1) 返回正确的权重(分别为 251 和 244),剩下要做的就是选择最高的(251)并将大金字塔的顶部值添加到它(4 )。结果是 255。

我们所做的是减少一个问题(计算高度为 5 的金字塔的最大重量),这样我们就剩下两个较小的问题需要解决(计算高度为 4 的金字塔的最大重量)。同样的方法,我们可以将高度 4 的问题简化为解决高度 3 的相同问题。例如,getWeight(1, 1) 将进行两次递归调用 getWeight(2, 1)getWeight(2, 2)

       1  <--- (2, 1)      3  <--- (2, 2)
      / \                 / \
    92   41             41   44
   /  \ /  \           /  \ /  \
142    6    4         6    4    8

getWeight(1, 1) 应该返回 244 = 9 + max(235, 55)。

继续这样下去,我们最终解决了高度为 1 的金字塔的问题。这些是原始金字塔底部的值(8、142、6、4 和 8)。递归到此结束;高度为 1 的金字塔只不过是一个节点。该节点的值是通过该金字塔的(唯一)路径的权重。

【讨论】:

  • 是从上到下计算所有路径吗?那么它比较哪个是最佳的?
  • 尝试了所有的可能性。但是我们并没有完整计算每条路径的权重。每当一个“亚金字塔”比它的邻居轻时,它就会被丢弃。只有最重的子路径会“冒泡”以参与计算从金字塔更高处开始的“更大”路径。
  • 你能解释一下你对金字塔下的说法是什么以及丢弃路径如何给出写解决方案
  • 我添加了一个“工作原理”部分来解释子金字塔;希望对您有所帮助。
猜你喜欢
  • 1970-01-01
  • 2012-07-29
  • 1970-01-01
  • 2019-08-27
  • 1970-01-01
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多