正在加载

请稍等

第一次加载可能稍慢

P1006 传纸条 题解

Author: ljc Time: 2025/09/21 Tag: 题解

P1006 [NOIP 2008 提高组] 传纸条

题目大意

给你一个n*m的数字矩阵,你需要从左上到右下走两次,只能向下或向右走,且除了点(1,1)(n,m)外没有其他路过的点相同。求这种走路方案路过点的权值和最大。

(有点曲解题意但对做题不影响)

提示1

数据量虽然不大(只有50)但显然暴力是不够看的。这里使用动态规划,那么请查看这张图。

asdfasdfasfsdfasdfsdfd

题解

如图,我们每次搜索一根最前端的红线的两个不相同的点进行转换。

观察发现:对于每条横线的两个点$(i_1,j_1),(i_2,j_2)$,满足:$i_1+j_i=i_2+j_2$。

红线的数量为:$n+m-1$。

由此,我们可以设$F_{(i,j),(x,y)}$为某条横线上的两个点$(i,j),(x,y)$,即满足$i+j=x+y$且$iy$,点权值为$A_{i,j}$,

则状态转移方程为:

当两点不在红线上相邻的点时: $$ F_{(i,j),(x,y)} = \max(F_{(i-1,j),(x-1,y)},F_{(i-1,j),(x,y-1)},F_{(i,j-1),(x-1,y)},F_{(i,j-1),(x,y-1)})+A_{i,j}+A_{x,y} $$ 当两点在红线上相邻时:(去掉两点选择相同节点的情况) $$ F_{(i,j),(x,y)} = \max(F_{(i-1,j),(x-1,y)},F_{(i-1,j),(x,y-1)},F_{(i,j-1),(x,y-1)})+A_{i,j}+A_{x,y} $$ 可是这样的四维数组浪费的空间非常大,所以我们直接用一个$k$压掉一维,变成$F_{k,j,y}$,使用$k$计算出$i,x$(当然去掉j、y那两维也可以):$k+1=i+j=x+y$ => $i=k-j+1,x=k-y+1$。

Code

#include<bits/stdc++.h>
#define N 105
using namespace std;

int n,m,a[N][N],f[N][N][N];

int max(int a,int b,int c,int d){  // 扩大一下max函数
    if(a>=b and a>=c and a>=d)return a;
    if(b>=a and b>=c and b>=d)return b;
    if(c>=a and c>=b and c>=d)return c;
    return d;
}

int main(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin >> a[i][j];
        }
    }
    for(int k=1;k<=n+m-2;k++){
        for(int i=min(k,n),j=k-i+1;i>=1&&j<=min(k,m);i--,j++){
            for(int x=i-1,y=j+1;x>=1&&y<=min(k,m);x--,y++){ // 限制变量
                if(y==j+1){  // 两种情况转移
f[k][j][y] = max(f[k-1][j-1][y],f[k-1][j][y],f[k-1][j-1][y-1],-1234567890)+a[i][j]+a[x][y];
                }else{
f[k][j][y] = max(f[k-1][j-1][y],f[k-1][j][y-1],f[k-1][j][y],f[k-1][j-1][y-1])+a[i][j]+a[x][y];
                }
            }
        }
    }
    cout << f[n+m-2][m-1][m] <<endl;
    return 0;
}

声明:本博客由作者原创,转载请标明出处。