51nod 1084+1083 矩阵取数问题 dp

先来说这个比较简单的问题:

一个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[n][n]);
    }
    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][n][n]);
    }
    return 0 ;
}



PS:其实还做过一个类似的dp题目,要求的是计算从左上角走到右下角以及从左上角走到右上角的总价值的最大值,其中两条路可以不同步,并且经过的同一个位置的价值变为0.

具体看连接点击打开链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值