本题 \(n \leq 100\) ,我们先分析一下 大力搜索 的效率。
众所周知, 从 \(1,1\) 走到 \(n,m\) (每次只能向右走或向下走) 的方案数为:
简单解释:共走 \(n+m\) 步,其中 \(n\) 步向下, \(m\) 步向右。而:
组合即可。
那么,搜索的时间复杂度 至少是 \(O(C_{2 \times n}^n)\).
大概算一下:
这个数很大,是我们无法接受的。
所以, 下面考虑\(dp\).
用 \(dp_{i,j,k,l}\) 表示 从左上角到右下角经过\(i,j\),再从右下角到左上角经过\(k,l\)的最大路径。
首先有一个结论: \(dp_{i,j,k,l}=dp_{k,l,i,j}\)
这是显然的,因为交换两个点值并不会影响答案。
那么,显然对于 \(i,j\) ,从 \(i,j-1\) 和 \(i-1,j\) 考虑转移。
对于 \(k,l\) ,从 \(k+1,l\) 和 \(k,l+1\) 考虑转移。
不难得出:
也就是从相邻的四个格子加上当前两个格子的值。
而我们之前的结论是 \(dp_{i,j,k,l}=dp_{k,l,i,j}\)
又这条路径必然会经过 \((n-1,m)和(n,m-1)\) ,
所以答案为
时间复杂度为\(O(n^2 \times m^2)\),空间复杂度为\(O(n^2 \times m^2)\),可以通过本题。
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[51][51],dp[51][51][51][51];
int max_four(int a,int b,int c,int d){
return max(max(max(a,b),c),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 i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=n;k++)
for(int l=j+1;l<=m;l++)
dp[i][j][k][l]=max_four(dp[i][j-1][k-1][l],dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k][l-1])+a[i][j]+a[k][l];
cout<<dp[n][m-1][n-1][m];
return 0;
}
下面考虑一个加强:
如果 \(n,m \leq 100\) 呢?
这时上述算法的空间与时间都很不理想,有超时和爆空间的可能。
对于上述的 \(dp_{i,j,k,l}\) ,你会发现:
\(i+l=j+k\)
这就是 走的步数。
那么,我们只需让 \(step=i+l=j+k\) ,
然后用 \(dp_{step,i,j}\) 进行转移即可。把上面的状态转移方程中的 \(k\) 改为 \(step-j\) ,\(l\) 改为 \(step-i\). 所以:
时间复杂度为 \(O(nm \times (n+m))\),空间复杂度为 \(O(nm \times (n+m))\).
#include<bits/stdc++.h>
using namespace std;
int n,m;
int dp[101][51][51],a[51][51];
//因为step最大会达到2*n-1,所以数组要开大
int max_four(int a,int b,int c,int d){
return max(max(max(a,b),c),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 step=1;step<=n+m-1;step++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(step-i+1<1 || step-j+1<1) continue; //去掉k和l是负数的情况
dp[step][i][j]=max_four(dp[step-1][i][j],dp[step-1][i-1][j-1],dp[step-1][i][j-1],dp[step-1][i-1][j])+a[i][step-i+1]+a[j][step-j+1];
if(i==j) dp[step][i][j]-=a[i][step-i+1]; //如果i=j,说明两点重合,需要减掉一个
}
cout<<dp[n+m-1][n][n];
return 0;
}