51 Nod 1083 矩阵取数问题(动态规划)

原题链接https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1083

题目分析:通过读题发现我们只能往右边或者下边走,意味着“不走回头路”,就是说矩阵里面每个位置最多只会经过一次。其实很多地方是“没有机会”经过的。比如我现在在第 x 行的第 y 列,不管之前走的路径是什么样子,则它左边和上边的位置都是不可能再走到的。也就是说,我先在在矩阵第 x 行的第 y 列,并假设以它为原点把矩阵分成四个“象限”,只有第四象限的位置才有可能从这以后经过 (当然还包括横轴的正半轴)!

假设我们从起点走到终点的过程中经过第 x 行的第 y 列某个位置,为了从起点到终点得到的和最大,那么从起点到第 x 行的第 y 列这个位置经过的数的和也一定要最大。我们定义集合 A 是从起点到第 x 行的第 y 列的全部路径集合,定义集合 B 是从第 x 行的第 y 列到终点的全部路径集合。那么起点到终点的路径实际上是子路径 aA 和子路径 bB 的连接(注意删掉第 x 行的第 y 列这个点,否则走了两次了)。 即所有经过第 x 行的第 y 列的路径都可以划分到 A 和 B 这两个集合里,而且任何 aA 和子路径 bB 都可以拼接出一条经过第 x 行的第 y 列的路径。那么我要选择一条经过 x,y 的能得到最大值的路径,显然要选择 A 集合里路径和最大的 a,(其实还要选 B 集合里和路径和最大的 b)。

说了这么多,其实就是想明确一个事:从起点到终点的最优路径上经过了 (m + n - 1) 个点,则这条路径上对应起点到这 (m + n - 1) 个点的子路径也都从起点到该位置的所有路径中和最大的路径。

那么假设我们定义 f(int \, x, int \, y) 表示从起点到第 x 行的第 y 列的最优路径上的数之和,并假设这个矩阵事个二维数组 A[\, ][ \, ] (下标从 1 开始)。我们考虑一下,我们如何才能到(x,y) ?前一步要么到 (x -1, y), 要么到 (x, y -1) ,因为有且只有这两个位置能到 (x,y),那么怎样才能得到 f(x,y) ? 按我们前面说的,如果从起点达到 (x,y) 的最优路径要经过 (x -1, y)或者 (x, y -1)则,从起点到达 (x, y -1) 或者 (x -1, y) 的路径一定也必须是最优的。那么按照我们对 f 的定义,我们有从起点达到 (x,y) 的最优路径有两种可能:

  • 要么 f(x - 1, y) + A[x][y]
  • 要么 f(x, y - 1) + A[x][y]

我们要取最优,那自然取较大的,因此有 f(x, y) = max(f(x - 1, y) , f(x, y - 1) ) + A[x][y] 。这样原来要枚举指数条路径,现在对于每个位置只有两种情况啦。有了递推关系还不够,有初值才能求解。那我们看一下f(1,1),显然这是在起点,没的选f(1,1) = A[1][1]。那么按照递推式 f(1,2) = max(f(0, y) , f(1,1)) + A[1][2] , 但是我们对f(0, y) 没有定义呀!考虑下实际意义,这表示要么我们从上面到达 (1,2) ,要么从左面到达 (1,2)。可是上面没有位置过来啊,这种说明没的选。所以我们可以定义f(0, y) = -\infty, 同理我们也可以定义f(x, 0) = -\infty

那么总结一下我们的递推式:

f(x, y) = \left\{\begin{matrix} -\infty & & (x = 0\, || \,y = 0 )\\ A[1][1]& & (x = 1 \,and\,y = 1)\\ Max(f(x-1,y),f(x, y-1)) + A[x][y]& &(else) \end{matrix}\right.
分析一下这个算法的时间复杂度? 显然是 O(m * n),空间复杂度也一样。


代码如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int INFTY = (1 << 29);

int main() {
	int n, a[505][505], dp[505][505];
	cin >> n;
	
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= n; j++)
			cin >> a[i][j];
	
	for (int i = 0; i <= n; i++) {
		a[0][i] = INFTY;
		a[i][0] = INFTY;
	}
	
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == 1 && j == 1 ) dp[i][j] = a[1][1];
			else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
		}
	}	
	
	cout << dp[n][n] << endl;
	
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

laugh12321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值