给定一个m行n列的矩阵,矩阵每个元素是一个正整数,你现在在左上角(第一行第一列),你需要走到右下角(第m行,第n列),每次只能朝右或者下走到相邻的位置,不能走出矩阵。走过的数的总和作为你的得分,求最大的得分。


  初看此题,你的思路是什么?

 
(1) 贪心? 先走到大的数再说?看这个例子:
无论你以什么方式走到3,总和都是1 + 1 + 3 + 1 + 1 + 1 + 1 = 9
我们为了1个3,放弃了那么多个2, 不值啊。如果我们放弃3而走那些2, 得到的和是1 + 1 + 2 + 2 + 2 + 1 + 1 = 10
看来贪心是错的! 因为我们走到最大值时有很多值不能走到了,一个最大值会把我们带沟里去。
(2) 枚举?枚举是万能的嘛。

可以试试,有多少条可能的路径?

你需要往下走(m – 1)次,往右走(n – 1)次,才能走到右下角,一共走m + n – 2步!可行路径的总条数有C(m + n – 2, m - 1)。
每条路径长度是m + n – 1 ,所以总的时间复杂度是O(C(m + n – 2, m – 1) * (m + n – 1)), 试试看m = n = 100时,这个数有多大吧!
没办法了么?
 
 (19899)=22750883079422934966181954039568885395604168260154104734000
想想看,问题有什么性质?

我们只能往右边或者下边走,意味着“不走回头路”,就是说矩阵里面每个位置最多只会经过一次。其实很多地方是“没有机会”经过的。比如我现在在第x行第y列,不管之前走的路径是什么样子,则它左边和上边的位置都是不可能再走到的,对吗?也就是说,我先在在矩阵第x行第y列,并假设以它为原点把矩阵分成四个“象限”,只有第四象限的位置才有可能从这以后经过 (当然还包括横轴的正半轴)!
 
假设我们从起点走到终点的过程中经过第x行第y列某个位置,为了从起点到终点得到的和最大,那么从起点到第x行第 y列这个位置经过的数的和也一定要最大。这几乎是显然的,但是你要刨根问底的话,可能要问为什么会这样呢?
我们定义集合A是从起点到第x行第y列的全部路径集合,定义集合B是从第x行第y列到终点的全部路径集合。那么起点到终点的路径实际上是子路径a∈A和子路径b∈B的连接(注意删掉第x行第y列这个点,否则走了两次了,呵呵)。

即所有经过第x行第y列的路径都可以划分到A和B这两个集合里,而且任何a∈A和子路径b∈B都可以拼接出一条经过第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 – 1,y)或者(x,y – 1)的路径一定也必须是最优的。
那么按照我们对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) = -∞, 同理我们也可以定义f(x, 0)  = -∞。

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

分析一下这个算法的时间复杂度? 显然是O(m * n),空间复杂度也一样,因为我们打出了一张(m + 1) * (n + 1)的表格——因此空间也是O(m * n)的。

再走一步?

我们找到了最大的和,如何得到和最大的路径呢? 还是从递推式入手,我们发现如果f(x,y) = f(x – 1,y) + A[x][y] 则它是从上面过来的,所以前一个位置是(x – 1, y)
否则 f(x, y) = f(x, y – 1) + A[x][y]则它是从左面过来的,所以前一个位置是(x, y- 1)。
这看起来最优路径被唯一确定了? 不是的,事实上当f(x – 1,y) = f(x, y – 1)时,前一个位置在上面或者左面都可以——所以路径还是很多很多的!

通过这个例子,你是不是对动态规划有了更深刻的理解呢?

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,a[][],dp[][];
int main()
{
cin>>n;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
cin>>a[i][j];
dp[i][j]=-;
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
dp[i][j]=max(dp[i][j-],dp[i-][j])+a[i][j];
cout<<dp[n][n];
}

如果对你有所帮助,别忘了加好评哦;么么哒!!下次见!88

- > 动规讲解基础讲解四——矩阵取数的更多相关文章

  1. 51Nod 1083 矩阵取数问题(矩阵取数dp,基础题)

    1083 矩阵取数问题 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下 ...

  2. 1084 矩阵取数问题 V2

    1084 矩阵取数问题 V2 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下 ...

  3. NOIP2007 矩阵取数游戏

    题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2. ...

  4. NOIP2007矩阵取数[DP|高精度]

    题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2. ...

  5. TYVJ 矩阵取数 Label:高精度+dp

    题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2. ...

  6. 【NOIP2007】矩阵取数

    因为傻逼写错高精度搞了一下午浪费好多时间,好想哭qaq 原题: 帅帅经常更同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij据为非负整数.游戏规则如下: 1. 每次取数时须从每 ...

  7. 51nod1084 矩阵取数问题 V2

    O(n4)->O(n3)妈呀为什么跑这么慢woc #include<cstdio> #include<cstring> #include<cctype> #i ...

  8. 1166 矩阵取数游戏[区间dp+高精度]

    1166 矩阵取数游戏 2007年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description [ ...

  9. 矩阵取数游戏 NOIP 2007

    2016-05-31 17:26:45 题目链接: NOIP 2007 矩阵取数游戏(Codevs) 题目大意: 给定一个矩阵,每次在每一行的行首或者行尾取一个数乘上2^次数,求取完最多获得的分数 解 ...

随机推荐

  1. srand()

    //第一次:5 0 第二次:5 16 srand(); //seed为常数,则每次运行产生的随机数一样 printf(); //产生的随机数都是一样的,都是5 srand(time(NULL)); p ...

  2. K Seq HihoCoder - 1046 || BZOJ4504 k个串

    这题与超级钢琴类似,然而重复的不重复计算贡献.. 那么先求出数组nxt,nxt[i]表示第i个元素之后的第一个与其相等的元素的下标,不存在则nxt[i]=0 考虑取的区间左端点为1时的情况. 将读入序 ...

  3. 块级标签与预格式化文本标签----------大多数XHTML可以表示为两种类型的标签:块标签(block tag)和内联标签(inline tag)

    <html> <head> <meta charset="utf-8"> <title>块级标签</title> < ...

  4. unix_12c_db_init

    sample 1: add a new cdb 1.for the new db  Aprod please apply two new direcotry in Cdb3/Cdb4/Aprod  5 ...

  5. Storm编程入门API系列之Storm的可靠性的ACK消息确认机制

    概念,见博客 Storm概念学习系列之storm的可靠性  什么业务场景需要storm可靠性的ACK确认机制? 答:想要保住数据不丢,或者保住数据总是被处理.即若没被处理的,得让我们知道. publi ...

  6. MySQL客户端导入数据库脚本,字段值出现乱码解决方法

    解决方法1:在MySql安装目录下找到my.ini,将[mysql]下的default-character-set=latin1改为default-character-set=utf8,保存,然后重启 ...

  7. python自动化--模块操作之re、MySQL、Excel

    一.python自有模块正则 import re # re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None print(re.match("www ...

  8. Sql server 本地临时表、全局临时表的区别

    创建了本地临时表#Tmp.全局临时表##Tmp: 本地临时表: 取名以#开头.如:#Tmp(会自动加上一串后缀) 只有当前的连接可以访问 连接关闭后,本地临时表自动释放 全局临时表: 取名以##开头. ...

  9. 5步上手体验kettle快捷调度方式

    https://my.oschina.net/u/944575/blog/1557410 kettle调度监控最佳实践 https://my.oschina.net/u/1026947/blog/15 ...

  10. Mysql导入导出大量数据的方法、备份恢复办法

    经常使用PHP+Mysql的朋友一般都是通过phpmyadmin来管理数据库的.日常的一些调试开发工作,使用phpmyadmin确实很方便.但是当我们需要导出几百兆甚至几个G的数据库时,phpmyad ...