方格取数

AcWing 1027. 方格取数

这题不能分两次走,

分开走(错误)

见如下代码

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

const int N = 50;

int a[N][N];
int f[N][N];
int pre[N][N];//1表示从上面来 0表示从左边来

int main(){
    int n;
    cin >> n;
    int i,j,c;
    while(cin>>i>>j>>c,i||j||c){
        a[i][j]=c;
    }
    memset(f,-1,sizeof(f));
    memset(pre,-1,sizeof(pre));
    f[1][0]=f[0][1]=0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(f[i-1][j]>f[i][j-1]){
                f[i][j]=f[i-1][j]+a[i][j];
                pre[i][j]=1;
            }else{
                f[i][j]=f[i][j-1]+a[i][j];
                pre[i][j]=0;
            }
        }
    }
    pre[1][1]=-1;
    int ans = f[n][n];
    int nowi=n,nowj=n;
    while(1){
        a[nowi][nowj]=0;
        //如果从上面来
        if(pre[nowi][nowj]==1){
            --nowi;
        }
        else if(pre[nowi][nowj]==0){
            --nowj;
        }else break;
    }
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            f[i][j]=max(f[i-1][j],f[i][j-1])+a[i][j];
        }
    }
    ans+=f[n][n];
    cout << ans << '\n';
    return 0;
}

当输入

7
1 3 2
1 4 3
2 3 3
3 3 3
5 5 4
6 5 4
7 3 2
7 5 4
0 0 0
会输出23
而正确答案是25

因此我们需要同时走;

正解

考虑用 f ( i 1 , j 1 , i 2 , j 2 ) f(i_1,j_1,i_2,j_2) f(i1,j1,i2,j2)

来 表 示 点 ( i 1 , j 1 ) 和 点 ( i 2 , j 2 ) 来表示点 (i_1,j_1) 和点(i_2,j_2) (i1,j1)(i2,j2)

因为同一个点只能取一次,第二次就变成0了;

只有当 i 1 + j 1 = = i 2 + j 2 i_1+j_1 == i_2+j_2 i1+j1==i2+j2的时候

才有可能出现 i 1 = = i 2 且 j 1 = = j 2 i_1==i_2且j_1==j_2 i1==i2j1==j2

因此我们令 k = i 1 + j 1 = i 2 + j 2 k = i_1+j_1 = i_2+j_2 k=i1+j1=i2+j2

现在我们可以将四维DP优化到三维DP了

f ( k , i 1 , i 2 ) 来 替 代 f ( i 1 , j 1 , i 2 , j 2 ) f(k,i_1,i_2) 来替代f(i_1,j_1,i_2,j_2) f(k,i1,i2)f(i1,j1,i2,j2)

Code

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

const int N = 50;

int a[N][N];
int f[N<<1][N][N];

int main(){
    int n;
    cin >> n;
    int i,j,c;
    while(cin>>i>>j>>c,i||j||c){
        a[i][j]=c;
    }
    for(int k=2;k<=n<<1;++k){
        for(int i1=1;i1<=n;++i1){
            for(int i2=1;i2<=n;++i2){
                int j1 = k - i1,j2 = k - i2,sum;
                if(j1<1||j1>n||j2<1||j2>n) continue;
                //只能加一次
                if(i1==i2){
                    sum = a[i1][j1];
                }
                else{
                    sum = a[i1][j1]+a[i2][j2];
                }
                //下下、下右、右下、右右四种情况
                int &t = f[k][i1][i2];
                t = max(t,f[k-1][i1-1][i2-1]+sum);
                t = max(t,f[k-1][i1][i2-1]+sum);
                t = max(t,f[k-1][i1-1][i2]+sum);
                t = max(t,f[k-1][i1][i2]+sum);
            }
        }
    }
    cout << f[n<<1][n][n] << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值