面试题:礼物的最大价值
题目:
在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)
可以从棋盘的左上角开始拿格子里的礼物,并每次向右或向下移动一格,直到到达棋盘的右下角
给定一个棋盘及其上面的礼物,计算你最多能拿到多少价值的礼物
即,给定一个二维矩阵,从左上角走到右下角,每次只能向右或者向下移动,经过的格子的数的最大总和
思路:
使用辅助数组maxa来记录每个位置到右下角的最大总和
1. 对于位置(i,j)= a,有两种走法,一种是(i+1,j),一种是(i,j+1),如果分别知道了从这两个位置开始,到右下角路径的最大总和b和c,就可以用max(a+b,a+c)得到从当前位置到右下角路径的最大总和
2. 采用自下向上的思想
可知从右下角开始到右下角,只包含其本身,所以可以得max[m-1][n-1] = a[m-1][n-1]
由1.得,知道了一个格子的值以后,可以计算其上面和左边的格子的值
左图为计算批次,
第一轮count=0,计算右下角的5
第二轮count=1,可由右下角5计算其上面和左边的格子
第三轮count=2,可由新计算出的两个数,计算其上面和左面的数
因此可以用一个循环来为maxa进行赋值
具体做法:
1.
由左图可得规律,当用count来计数轮数时,当前轮数中所需要计算的坐标(i,j)总是满足i+j=m+n-2-count (-2是因为数组从0开始,所以是m-1+n-1-count)
因此,每次循环时,从最下面的格子开始计算(也就是最左,因为i+j是固定的数,i越大,j越小),依次往右上计算
注意:需要判断j的越界
2.
每个格子计算时需要注意:
1)是否在最下面:如果是处于最下面的格子,则可供此位置选的路径只有往右走,所以直接用当前位置的值+右边格子的最大路径即可
2)是否在最右边:如果是处于最右的格子,则可供此位置选的路径只有往下走,所以直接用当前位置的值+下面格子的最大路径即可
3)不在最下面也不在最右边:处于中间的格子,可供此位置选的路径有往下走和往右走,此时选两个之中最大的值,相加即可
public class Q47 {
public static void main(String[] args) {
int[][] a = new int[][]{{1,10,3,9},{12,2,9,6},{5,7,4,11},{3,7,16,5}};
System.out.printf("\nresult:%d",maxGift(a));
}
public static int maxGift(int[][] a) {
if(a.length==0||a[0].length==0) {
return 0;
}
int count=0;
int m = a.length;
int n = a[0].length;
int[][] maxa = new int[m][n];
maxa[m-1][n-1] = a[m-1][n-1];
while(count<m+n-2) {
count++;
System.out.printf("count:%d\n",count);
int loc = m + n - count - 2;
// 分批次对每个位置的最大价值进行赋值
for(int i=m-1;i>=0;i--) {
int j = loc-i;
// 如果j<0 则直接i--
if(j>=0) {
// 判断j是否越界
if(j==n) {
break;
}
// 三种情况
if(i==m-1) {
maxa[i][j] = a[i][j]+maxa[i][j+1];
}else
if(j==n-1) {
maxa[i][j] = a[i][j]+maxa[i+1][j];
}else {
if(maxa[i+1][j]>maxa[i][j+1]) {
maxa[i][j] = a[i][j]+maxa[i+1][j];
}else {
maxa[i][j] = a[i][j]+maxa[i][j+1];
}
}
//System.out.printf("(%d,%d):%d\n",i,j,maxa[i][j]);
}
}
}
return maxa[0][0];
}
}