我在编程论坛上发现了这个问题:
给出由N * M个细胞组成的表,每个细胞具有一定量的苹果。你从左上角开始。在每个步骤中,您可以向下或向右移动一个单元格。如果您从左上角移动到右下角,请设计算法以查找可以收集的最大苹果数。
我已经想到了三种不同的复杂性[在时间和方面;空间]:
方法1 [最快]:
for(j=1,i=0;j<column;j++)
apple[i][j]=apple[i][j-1]+apple[i][j];
for(i=1,j=0;i<row;i++)
apple[i][j]=apple[i-1][j]+apple[i][j];
for(i=1;i<row;i++)
{
for(j=1;j<column;j++)
{
if(apple[i][j-1]>=apple[i-1][j])
apple[i][j]=apple[i][j]+apple[i][j-1];
else
apple[i][j]=apple[i][j]+apple[i-1][j];
}
}
printf("\n maximum apple u can pick=%d",apple[row-1][column-1]);
方法2:
结果是临时数组,所有插槽最初为0.
int getMax(int i, int j)
{
if( (i<ROW) && (j<COL) )
{
if( result[i][j] != 0 )
return result[i][j];
else
{
int right = getMax(i, j+1);
int down = getMax(i+1, j);
result[i][j] = ( (right>down) ? right : down )+apples[i][j];
return result[i][j];
}
}
else
return 0;
}
方法3 [最少使用空间]:
它不使用任何临时数组。
int getMax(int i, int j)
{
if( (i<M) && (j<N) )
{
int right = getMax(i, j+1);
int down = getMax(i+1, j);
return apples[i][j]+(right>down?right:down);
}
else
return 0;
}
我想知道哪种方法可以解决这个问题?
答案 0 :(得分:3)
方法1和方法2之间没有什么区别,方法1可能更好一些,因为它不需要堆栈用于接近2使用的递归,因为它会倒退。
方法3具有指数时间复杂度,因此它比具有complexitx O(行*列)的其他两个更糟糕。
您可以制作方法1的变体,沿对角线前进,仅使用O(max {rows,columns})额外空间。
答案 1 :(得分:0)
在时间方面,解决方案1是最好的,因为没有递归函数。 递归函数的调用需要时间
答案 2 :(得分:0)
你真的需要临时数组为N吗?
没有
如果初始的2-d数组有N列和M行,我们可以使用长度为M的1-d数组来解决这个问题。
在第一种方法中,您可以随时保存所有小计,但是当您移动到下一列时,您实际上只需要知道左侧和上方单元格的苹果值。一旦你确定了,你就不会再看那些先前的细胞了。
然后解决方案是在下一列开始时写下旧值。
代码看起来如下(我实际上不是C程序员,所以请耐心等待):
int getMax()
{
//apple[][] is the original apple array
//N is # of columns of apple[][]
//M is # of rows of apple[][]
//temp[] is initialized to zeroes, and has length M
for (int currentCol = 0; currentCol < N; currentCol++)
{
temp[0] += apple[currentCol][0]; //Nothing above top row
for (int i = 1; i < M; i++)
{
int applesToLeft = temp[i];
int applesAbove = temp[i-1];
if (applesToLeft > applesAbove)
{
temp[i] = applesToLeft + apple[currentCol][i];
}
else
{
temp[i] = applesAbove + apple[currentCol][i];
}
}
}
return temp[M - 1];
}
注意:没有任何理由将applesToLeft和applesAbove的值实际存储到局部变量中,并且可以随意使用? :赋值语法。
此外,如果列数少于行数,则应旋转此列,以使1-d数组的长度更短。
这样做是对第一种方法的直接改进,因为它节省了内存,而且在同一个1-d数组上迭代真的有助于缓存。
我只能想到使用不同方法的一个原因:
要获得多线程解决此问题的好处,您的第二种方法就是正确的。
在第二种方法中,您使用备忘录存储中间结果。
如果你的备忘录是线程安全的(通过锁定或使用无锁的哈希集),那么你可以启动多个线程,试图获得右下角的答案。
[//编辑:实际上,因为将int分配给数组是一个原子操作,我认为你根本不需要锁定。)
每次调用getMax都会随机选择是先执行getMax还是getMax。
这意味着每个线程都在问题的不同部分工作,因为有备忘录,它不会重复不同线程已经完成的工作。