本来是想学习一下双向dp的,然后搜到了这题,刚开始看到这题直观感觉是搜索,但是DFS==TLE,BFS+优先队列==Wa。后来想到不能往右走这个重要点,那就用dp走!还是要好好学习啊
思路:
走法有三种,无非就是从当前点的下边过来,当前点的上边下来,当前点的左边过来。对于dp数组我们可以开成三维的。dp【i】【j】【k】表示在点(i,j)处从k方向过来的最大值。对于K,0<=K<=2;我们可以规定2是从左边过来。1是从下边过来,0是从上边过来。
单向dp,开三维写法(期间wa了两发,一次多次输入没有初始化,第二次没有考虑到dp值为负数,所以初始化dp值应为-inf),需要优化
#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=106;
const int inf=0x3f3f3f3f;
int dp[N][N][N];
int a[N][N];
const int inf=0x3f3f3f3f;
int dp[N][N][N];
int a[N][N];
int main(){
int t;
int ca=1;
cin>>t;
while(t--){
memset(dp,-inf,sizeof(dp));
memset(a,0,sizeof(a));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
dp[1][1][0]=dp[1][1][1]=dp[1][1][2]=a[1][1];//初始化边界,最初的转移起点为(1,1)
for(int i=2;i<=n;i++)
dp[i][1][0]=dp[i-1][1][0]+a[i][1];//初始化第一列,对于这一列来说一个点只能是有上一行的同一位置上的点向下转移过来
for(int j=2;j<=m;j++){//枚举列
for(int i=1;i<=n;i++){//必须先枚举向左,正向向下遍历,这个点是由已经完成dp的前一列的值转移过来的
dp[i][j][2]=max(dp[i][j-1][0],max(dp[i][j-1][1],dp[i][j-1][2]))+a[i][j];
}
for(int i=n-1;i>=1;i--){//从下往上反向遍历,这样的话每一个点都是由这个点下一行的那个点转移过来的,在转移这个点的过程中需保证前一个点的该方向已经完成dp
dp[i][j][1]=max(dp[i+1][j][1],dp[i+1][j][2])+a[i][j];
}
for(int i=2;i<=n;i++){同理上,左右的遍历可以交换位置
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][2])+a[i][j];
}
}
printf("Case #%d:\n",ca++);
printf("%d\n",max(dp[1][m][0],max(dp[1][m][1],dp[1][m][2])));
}
}
int t;
int ca=1;
cin>>t;
while(t--){
memset(dp,-inf,sizeof(dp));
memset(a,0,sizeof(a));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
dp[1][1][0]=dp[1][1][1]=dp[1][1][2]=a[1][1];//初始化边界,最初的转移起点为(1,1)
for(int i=2;i<=n;i++)
dp[i][1][0]=dp[i-1][1][0]+a[i][1];//初始化第一列,对于这一列来说一个点只能是有上一行的同一位置上的点向下转移过来
for(int j=2;j<=m;j++){//枚举列
for(int i=1;i<=n;i++){//必须先枚举向左,正向向下遍历,这个点是由已经完成dp的前一列的值转移过来的
dp[i][j][2]=max(dp[i][j-1][0],max(dp[i][j-1][1],dp[i][j-1][2]))+a[i][j];
}
for(int i=n-1;i>=1;i--){//从下往上反向遍历,这样的话每一个点都是由这个点下一行的那个点转移过来的,在转移这个点的过程中需保证前一个点的该方向已经完成dp
dp[i][j][1]=max(dp[i+1][j][1],dp[i+1][j][2])+a[i][j];
}
for(int i=2;i<=n;i++){同理上,左右的遍历可以交换位置
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][2])+a[i][j];
}
}
printf("Case #%d:\n",ca++);
printf("%d\n",max(dp[1][m][0],max(dp[1][m][1],dp[1][m][2])));
}
}
双向dp写法(值得学习)
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1006;
int dpa[maxn][maxn],dpb[maxn][maxn];
int a[maxn][maxn];
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1006;
int dpa[maxn][maxn],dpb[maxn][maxn];
int a[maxn][maxn];
int main(){
int t;
cin>>t;
int ca=1;
while(t--){
memset(dpa,-inf,sizeof(dpa));
memset(dpb,-inf,sizeof(dpb));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
int t;
cin>>t;
int ca=1;
while(t--){
memset(dpa,-inf,sizeof(dpa));
memset(dpb,-inf,sizeof(dpb));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
//首先初始转移起点为0
dpa[0][1]=0;
//将转移的源头第一列全部设为a[][]
for(int i=1;i<=n;i++){
dpa[i][1]=dpa[i-1][1]+a[i][1];
}
dpa[0][1]=0;
//将转移的源头第一列全部设为a[][]
for(int i=1;i<=n;i++){
dpa[i][1]=dpa[i-1][1]+a[i][1];
}
//从第二列开始遍历dp转移
for(int j=2;j<=m;j++){
for(int i=1;i<=n;i++){
//dpa是由上到下搜索遍历的dp转移答案,只包含由前一结点向右向下转移得到的情况
dpa[i][j]=max(max(dpa[i][j-1],dpb[i][j-1]),dpa[i-1][j])+a[i][j];
}
for(int i=m;i>=1;i--){
//dpb反向遍历是由从下到上搜索遍历的dp转移答案,只包含由前一节点向上向右遍历转移得到的情况
dpb[i][j]=max(max(dpa[i][j-1],dpb[i][j-1]),dpb[i+1][j])+a[i][j];
}
}
for(int j=2;j<=m;j++){
for(int i=1;i<=n;i++){
//dpa是由上到下搜索遍历的dp转移答案,只包含由前一结点向右向下转移得到的情况
dpa[i][j]=max(max(dpa[i][j-1],dpb[i][j-1]),dpa[i-1][j])+a[i][j];
}
for(int i=m;i>=1;i--){
//dpb反向遍历是由从下到上搜索遍历的dp转移答案,只包含由前一节点向上向右遍历转移得到的情况
dpb[i][j]=max(max(dpa[i][j-1],dpb[i][j-1]),dpb[i+1][j])+a[i][j];
}
}
printf("Case #%d:\n",ca++);
printf("%d\n",max(dpa[1][m],dpb[1][m]));
}
}
printf("%d\n",max(dpa[1][m],dpb[1][m]));
}
}