剑指offer第二版-47.礼物的最大值(动态规划,广度优先遍历)C/C++版

问题描述在一个 m*n 的棋盘中的每一个格都放一个礼物,每个礼物都有一定的价值(价值大于0).你可以从棋盘的左上角开始拿各种里的礼物,并每次向左或者向下移动一格,直到到达棋盘的右下角。给定一个棋盘及上面个的礼物,请计算你最多能拿走多少价值的礼物?在这个棋盘中,按照(1,12,5,7,7,16,5)的顺序可以拿到总价值最大的礼物。解题思路1、动态规划思维对于这种路径问题,一般第一考虑...
摘要由CSDN通过智能技术生成

问题描述

在一个 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;

}

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值