51nod 1084+1083 矩阵取数问题 dp
2016-02-28 14:54
260 查看
先来说这个比较简单的问题:
一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下向右走,求能够获得的最大价值。
例如:3 * 3的方格。
1 3 3
2 1 3
2 2 1
能够获得的最大价值为:11。
经典并且简单的问题,dp[i][j]表示的是从位置(1,1)到位置(n,n)的最大价值。
比较麻烦的是第二个问题:
一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。
例如:3 * 3的方格。
1 3 3
2 1 3
2 2 1
这个问题就不是和上面的一样了,要求的是先到右下角然后返回左上角的最大价值,并且要是经过相同的位置该位置的价值只能取一次。
从左上角到右下角再回到左上角,这个过程可以看作从左上角同时走两路到右下角,我们记录两路当前的位置,这样就可以处理“同一个格子的数只能取一次”了。
1. 假设是m行, n列的矩阵,那么从左上角,走到右下角,需要的步数就是, (m + n)步;当有两条路径的时候,也就是i + j = p + q
2. 保证两条线不相交:
假设下一个目标节点是(i, j, p, q)那么需要推导的点就是
(i - 1, j) (p - 1, q)
(i, j - 1) (p, q - 1)
因此 max(dp[i - 1][j][p - 1][q], dp[i - 1][j][p][q - 1],
dp[i][j - 1][p - 1][q], dp[i][j - 1][p][q - 1]) + val[i][j] + val[p][q]
3. 优化:
因为知道步数之后, 知道行坐标, 即可推算出纵坐标
那么, 优化后的dp方程为
k = i + j = p + q
其实需要判定列的原因是为了`取`val中的值
j = k - i;
q = k - q;
那么对于行 存在两种可能 (i - 1, p) (i, p - 1), (i - 1, p - 1), (i, j) 最后一种情况是, 列变化了
dp[k][i][p] = max (dp[k - 1][i - 1][p], dp[k - 1][i][p - 1], dp[k - 1][i - 1][p - 1], dp[k - 1][i][p]) + val[i][j] + val[p][q]
其中i != p
PS:其实还做过一个类似的dp题目,要求的是计算从左上角走到右下角以及从左上角走到右上角的总价值的最大值,其中两条路可以不同步,并且经过的同一个位置的价值变为0.
具体看连接点击打开链接
一个N*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,从左上走到右下,只能向下向右走,求能够获得的最大价值。
例如:3 * 3的方格。
1 3 3
2 1 3
2 2 1
能够获得的最大价值为:11。
经典并且简单的问题,dp[i][j]表示的是从位置(1,1)到位置(n,n)的最大价值。
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; int const maxn = 505 ; int a[maxn][maxn]; int dp[maxn][maxn] ; //dp[i][j]表示的是从位置(1,1)到位置(i,j)所获得的最大价值 int main() { int n; while(scanf("%d",&n)!=EOF) { memset(dp,0,sizeof(dp)); for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= n ; j++) { scanf("%d",&a[i][j]); } } for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <=n ; j++) { dp[i][j] = max(dp[i-1][j],dp[i][j-1])+a[i][j] ; } } printf("%d\n",dp ); } return 0; }
比较麻烦的是第二个问题:
一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。
例如:3 * 3的方格。
1 3 3
2 1 3
2 2 1
这个问题就不是和上面的一样了,要求的是先到右下角然后返回左上角的最大价值,并且要是经过相同的位置该位置的价值只能取一次。
从左上角到右下角再回到左上角,这个过程可以看作从左上角同时走两路到右下角,我们记录两路当前的位置,这样就可以处理“同一个格子的数只能取一次”了。
1. 假设是m行, n列的矩阵,那么从左上角,走到右下角,需要的步数就是, (m + n)步;当有两条路径的时候,也就是i + j = p + q
2. 保证两条线不相交:
假设下一个目标节点是(i, j, p, q)那么需要推导的点就是
(i - 1, j) (p - 1, q)
(i, j - 1) (p, q - 1)
因此 max(dp[i - 1][j][p - 1][q], dp[i - 1][j][p][q - 1],
dp[i][j - 1][p - 1][q], dp[i][j - 1][p][q - 1]) + val[i][j] + val[p][q]
3. 优化:
因为知道步数之后, 知道行坐标, 即可推算出纵坐标
那么, 优化后的dp方程为
k = i + j = p + q
其实需要判定列的原因是为了`取`val中的值
j = k - i;
q = k - q;
那么对于行 存在两种可能 (i - 1, p) (i, p - 1), (i - 1, p - 1), (i, j) 最后一种情况是, 列变化了
dp[k][i][p] = max (dp[k - 1][i - 1][p], dp[k - 1][i][p - 1], dp[k - 1][i - 1][p - 1], dp[k - 1][i][p]) + val[i][j] + val[p][q]
其中i != p
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; int const maxn = 205 ; int a[maxn][maxn]; int dp[2*maxn][maxn][maxn] ; //dp[k][x1][x2] k是步数,x1是第一个走法的横坐标,x2是第二种走法的横坐标 int main() { int n,m ; while(scanf("%d%d",&m,&n)!=EOF) { memset(a,0,sizeof(a)); for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= m ; j++) { scanf("%d",&a[i][j]); } } memset(dp,0,sizeof(dp)); int d ; for(int x1 = 1 ; x1 <= n ; x1++) { for(int x2 = 1 ; x2 <= n ; x2++) { for(int k = max(x1,x2)+1 ; k <= min(x1,x2)+m ; k++) { if(x1!=x2)d = a[x1][k-x1]+a[x2][k-x2] ; else d = a[x1][k-x1] ; dp[k][x1][x2] = max(dp[k-1][x1-1][x2],max(dp[k-1][x1][x2-1],max(dp[k-1][x1-1][x2-1],dp[k-1][x1][x2])))+d; //cout<<dp[k][x1][x2]<<endl; } } } printf("%d\n",dp[m+n] ); } return 0 ; }
PS:其实还做过一个类似的dp题目,要求的是计算从左上角走到右下角以及从左上角走到右上角的总价值的最大值,其中两条路可以不同步,并且经过的同一个位置的价值变为0.
具体看连接点击打开链接
相关文章推荐
- noitatSsaG.134
- 有人串口转wifi模块 httpd client通信示例-用户使用网页通过服务器收发串口数据源码 小黄人软件
- Java Servlet(三):Servlet中ServletConfig对象和ServletContext对象
- POJ 2989 All Friends (极大团数量 + 全局变量局部变量)
- 容器的allocator(容器配置器)(一)
- sass(混合宏vs继承vs占位符)
- MFC中,编译器无法识别类等问题的另一种情况。
- LA 5009 三分
- sass继承 %占位符placeholder
- UVa--437 The Tower of Babylon(dp)
- find命令中参数perm的用法
- UVa 1583 - Digit Generator
- BZOJ_P1072 [SCOI2007]排列perm(状态压缩动态规划)
- ubuntu15.10 配置tftp
- Linux磁盘分区及要求
- ASP.NET中使用Nhibernate
- Linux内核的中断机制
- BZOJ 2154: Crash的数字表格
- Toefl作文
- sass混合宏