问题描述
在一个 m*n 的棋盘中的每一个格都放一个礼物,每个礼物都有一定的价值(价值大于0).你可以从棋盘的左上角开始拿各种里的礼物,并每次向左或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘及上面个的礼物,请计算你最多能拿走多少价值的礼物?
在这个棋盘中,按照(1,12,5,7,7,16,5)的顺序可以拿到总价值最大的礼物。
解题思路
1、动态规划思维
对于这种路径问题,一般第一考虑到的都是动态规划。分析题目可知,也就是求左上角到右下角的路径和为最大值的路径,并且每次只能往下或往右走。我们可以遍历它所有的路径,比较他们的路径和。
不难分析到,那么最大路径就是MAX(左边路径和,上边路径和)+当前值——到达某个点的路径最大值必定是当前值加上可到达此点的最大值。
即f(i,j)=f(i-1,j)+f(i,j-1)+currentValue
(1) 递归方式
既然公式都列出来了那么我们照着公式写代码就不难了,c++代码如下
/***
参数解析:
length gift的长度
width gift[length]的长度
indexlength 当前结点处于gift的位置
indexwidth 当前结点处于gift[indexlength]的位置*/
int Solution::getMaxValue(int **gift, int length, int width,int indexlength,int indexwidth) {
if (indexlength == 0 && indexwidth == 0)
return gift[indexlength][indexwidth];
else if (indexlength == 0 && indexwidth != 0)
return getMaxValue(gift, length, width,
indexlength, indexwidth - 1)+ gift[indexlength][indexwidth];
else if (indexlength != 0 && indexwidth == 0)
return getMaxValue(gift, length, width,
indexlength-1, indexwidth )
+ gift[indexlength][indexwidth];
return max(getMaxValue(gift, length, width, indexlength - 1, indexwidth),
getMaxValue(gift, length, width,
indexlength, indexwidth - 1))
+ gift[indexlength][indexwidth];
}
(2)循环方式
递归代码比较简洁易懂(但是我的好像看着密密麻麻一大块,可能是命名太长了的原因,不管了,哈哈)但是通常效率不高。如题所示,一个4*4的棋盘,用递归方式的话路径一共有A(4,4)=24种路径。但是循环方式的话从左到右以此循环只有一条路径。并且递归方式随棋盘的扩大而指数及扩大,循环方式路径永远等于1。
int Solution::getMaxValue(int **gift, int length, int width) {
int **value = new int*[length]; //构建一个二维辅助数组,用来记录到达value[i][j]的最大路径。
for (int i = 0; i<length; i++) {
value[i] = new int[width];
}
for (int i = 0; i<length; i++)
for (int j = 0; j<width; j++) {
int up = 0, left = 0;
if (i>0)
up = value[i - 1][j];
if (j>0)
left = value[i][j - 1];
value[i][j] = max(up, left) + gift[i][j];
}
int result = value[length - 1][width - 1];
for (int i = 0; i<length; i++)
delete[] value[i];
delete[] value;
return result;
}
有向图广度优先思路
如图说是,可以理解为这是一个有向图。那我们可以广度优先法遍历,然后在剪枝。其实代码和循环遍历差不多,稍微有一点点变化,效率稍微比循环低一丢丢。可以忽略不计。
代码如下:
int Solution::getMaxValue2(int **gift, int length, int width) {
class Node {
public:
int indexLength;
int indexWidth;
int sum;
Node(int x, int y, int s)
{
indexLength = x;
indexWidth = y;
sum = s;
}
};
queue<Node*> nodeQueue; //用一个队列来完成广度优先
nodeQueue.push(new Node(0, 0, gift[0][0]));
while (!(nodeQueue.front()->indexLength == length-1 && nodeQueue.front()->indexWidth == width-1)) { //遍历所有可能路径
Node *curNode = nodeQueue.front(); //出队操作
nodeQueue.pop();
int x = curNode->indexLength;
int y = curNode->indexWidth;
if(x!=length-1) //入队
nodeQueue.push(new Node(x + 1, y, curNode->sum + gift[x+1][y]));
if(y!=width-1)
nodeQueue.push(new Node(x, y + 1, curNode->sum + gift[x][y+1]));
}
int maxSum = 0;
while (!nodeQueue.empty()){ //取最大路径
Node *tempNode = nodeQueue.front();
nodeQueue.pop();
if (maxSum < tempNode->sum)
maxSum = tempNode->sum;
}
return maxSum;
}
测试代码
int main()
{
int **nums = new int*[4];
nums[0] = new int[4]{ 1,10,3,8 };
nums[1] = new int[4]{ 12,2,9,6 };
nums[2] = new int[4]{ 5,7,4,11 };
nums[3] = new int[4]{ 3,7,16,5 };
Solution s;
//循环测试
int result1 = s.getMaxValue(nums, 4, 4);
//递归测试
int result2 = s.getMaxValue(nums, 4, 4, 0, 0);
//有向图测试
int result3 = s.getMaxValue2(nums, 4, 4);
cout << "result1= " << result1 << endl;
cout << "result2= " << result2 << endl;
cout << "result3= " << result3 << endl;
system("pause");
return 0;
}
测试结果: