题目:
在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向左或向下移动一格,直到到达棋盘的右下角。给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?
分析:
这是一个动态规划问题,我们使用递归的思路分析。定义一个函数f(i,j)表示到达(i,j)的格子时能拿到的礼物总和的最大值。到达(i,j)有两种方式:通过格子(i-1,j)或通过格子(i,j-1),所以f(i,j)=max(f(i-1,j),f(i,j-1))+gift(i,j)。gift(i,j)表示坐标为(i,j)格子里礼物的价值。
因为递归有大量重复计算,所以我们使用递归分析思路,使用递推求解。
我们需要一个辅助二维数组,将中间的计算结果缓存进来。
这个问题还可以进一步优化,到达坐标(i,j)时候最大价值依赖于(i-1,j)和(i,j-1),因此第i-2行及上面的所有格子的最大价值实际上没必要保存下来,于是可以使用一个一维数组代替之前的二维数组。当我们计算(i,j)格子能拿到的最大价值的时候,一维数组中,前j个数字是f(i,0),f(i,1),……f(i,j-1),数组从下标j的数字到最后,分别为f(i01,j),f(i-1,j+1)……f(i-1,n-1)。也就是前j个保存当前行前j个位置的最大价值,后面的数字保存的是上一行n-j个位置的最大价值。
解法:
package com.wsy;
public class Main {
public static int[][] gift;
public static void main(String[] args) {
gift = new int[][]{{1, 10, 3, 8}, {12, 2, 9, 6}, {5, 7, 4, 11}, {3, 7, 16, 5}};
getMaxValue();
}
public static void getMaxValue() {
if (gift == null) {
return;
}
int rows = gift.length;
int cols = gift[0].length;
int[][] maxValue = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int left = 0;
int up = 0;
if (i > 0) {
up = maxValue[i - 1][j];
}
if (j > 0) {
left = maxValue[i][j - 1];
}
maxValue[i][j] = Math.max(up, left) + gift[i][j];
}
}
System.out.println("maxValue=" + maxValue[rows - 1][cols - 1]);
}
}
package com.wsy;
public class Main {
public static int[][] gift;
public static void main(String[] args) {
gift = new int[][]{{1, 10, 3, 8}, {12, 2, 9, 6}, {5, 7, 4, 11}, {3, 7, 16, 5}};
getMaxValue();
}
public static void getMaxValue() {
if (gift == null) {
return;
}
int rows = gift.length;
int cols = gift[0].length;
int[] maxValue = new int[cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
int left = 0;
int up = 0;
if (i > 0) {
up = maxValue[j];
}
if (j > 0) {
left = maxValue[j - 1];
}
maxValue[j] = Math.max(up, left) + gift[i][j];
}
}
System.out.println("maxValue=" + maxValue[cols - 1]);
}
}