动规:蒜头君的城堡之旅

蒜国地域是一个 n 行 m 列的矩阵,下标均从 1 开始。蒜国有个美丽的城堡,在坐标 (n,m) 上,蒜头君在坐标 (1,1) 的位置上。蒜头君打算出发去城堡游玩,游玩结束后返回到起点。在出发去城堡的路上,蒜头君只会选择往下或者往右走,而在返回的路上,蒜头君只会选择往上或者往左走,每次只能走一格。已知每个格子上都有一定数量的蒜味可乐,每个格子至多经过一次。

现在蒜头君请你来帮他计算一下,如何计划来回行程,可以收集到最多的蒜味可乐。

输入格式

第一行输入两个整数 n,m(1≤n,m≤50),表示蒜国是一个 n 行 m 列的矩阵。
接下来输入 n 行,每行输入 m 个整数,代表一个 n×m 的矩阵,每个整数代表对应位置上的蒜味可乐数量,每行的每两个整数之间用一个空格隔开。其中蒜头君的位置和城堡的位置上没有蒜味可乐,用 0 表示,其余位置上的整数范围在 [1,100] 内。

输出格式

输出一行,输出一个整数,表示蒜头君在来回路上能收集到的蒜味可乐的最大值。
样例输入
3 3
0 2 9
4 8 6
2 7 0
样例输出
36

题解:这个题刚开始想的是这样,先从上往下走,再记录下父亲,最后消去父亲的值,再从下往上走,记录结果,得出答案,但是只能过两组。

#include<bits/stdc++.h>

using namespace std;

const int maxn=55;

int mapp[maxn][maxn];
int dp1[maxn][maxn];
int pre[maxn][maxn];
int dp2[maxn][maxn];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&mapp[i][j]);
        }
    }
    //right down
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(i==1&&j==1){
                dp1[i][j]=0;
                pre[i][j]=-1;
            }else if(i==1){
                dp1[i][j]=dp1[i][j-1]+mapp[i][j];
                pre[i][j]=0;
            }else if(j==1){
                dp1[i][j]=dp1[i-1][j]+mapp[i][j];
                pre[i][j]=1;
            }else{
                if(dp1[i-1][j]>dp1[i][j-1]){
                    dp1[i][j]=dp1[i-1][j]+mapp[i][j];
                    pre[i][j]=1;
                }else{
                    dp1[i][j]=dp1[i][j-1]+mapp[i][j];
                    pre[i][j]=0;
                }
            }
        }
    }
    int ans=dp1[n][m];
    int tempr=n,tempc=m;
    while(pre[tempr][tempc]!=-1){
        if(pre[tempr][tempc]==1){
            mapp[tempr-1][tempc]=0;
            tempr=tempr-1;
        }else if(pre[tempr][tempc]==0){
            mapp[tempr][tempc-1]=0;
            tempc=tempc-1;
        }
    }
    for(int i=n;i>=1;i--){
        for(int j=m;j>=1;j--){
            if(i==n&&j==m){
                continue;
            }else if(i==n){
                dp2[i][j]=dp2[i][j+1]+mapp[i][j];
            }else if(j==m){
                dp2[i][j]=dp2[i+1][j]+mapp[i][j];
            }else{
                dp2[i][j]=mapp[i][j]+max(dp2[i+1][j],dp2[i][j+1]);
            }
        }
    }
    ans+=dp2[1][1];
    printf("%d\n",ans);
    return 0;
}

最后得出的分析:不能先算出去的最大值,再算返回的最大值,这样可能导致只保证了去的最大值,但没有保证来回之和的最大值。也就是说,来回的优先级是相同的,如果先算去路的最大值就会使得去的优先级高于回的优先级。

最后,可以使用一个四维数组res[i][j][k][l]来表示路径1走到[i][j]和路径2走到[k][l]的和最优值。其中,因为走的步数相同,所以i+j=k+l(利用这个把空间和时间复杂度降一级),两条路径也不能用到一个点,最后写出状态转移方程:res[i][j][k][l]=max(max(res[i-1][j][k-1][l],res[i-1][j][k][l-1]),max(res[i][j-1][k-1][l],res[i][j-1][k][l-1]))+mapp[i][j]+mapp[k][l],最后答案啊自然也得改一下,因为最后都走到终点,所以cout<<max(max(res[n-1][m][n-1][m],res[n-1][m][n][m-1]),max(res[n][m-1][n][m-1],res[n][m-1][n-1][m]))<<endl;

#include<bits/stdc++.h>

using namespace std;

const int maxn=55;

int mapp[maxn][maxn],res[maxn][maxn][maxn][maxn];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&mapp[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=1;k<=n;k++){
                for(int l=1;l<=m;l++){
                    if(i+j!=k+l){
                        continue;
                    }
                    if(i==k&&j==l){
                        continue;
                    }
                    res[i][j][k][l]=max(max(res[i-1][j][k-1][l],res[i-1][j][k][l-1]),max(res[i][j-1][k-1][l],res[i][j-1][k][l-1]))+mapp[i][j]+mapp[k][l];
                }
            }
        }
    }
    cout<<max(max(res[n-1][m][n-1][m],res[n-1][m][n][m-1]),max(res[n][m-1][n][m-1],res[n][m-1][n-1][m]))<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值