赞
踩
题目描述
设有 n×m 的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中的整数,求它能取到的整数之和的最大值。
输入
第一行有两个整数 n,m。
接下来 n 行每行 m 个整数,依次代表每个方格中的整数。
输出
一个整数,表示小熊能取到的整数之和的最大值。
输入样例
3 4
1 -1 3 2
2 -1 4 -1
-2 2 -3 -1
输出样例
9
记忆化搜索的优点
本题20个测试点总耗时1400ms,单个测试点最大耗时为320ms
记忆化搜索重要的一点是搞明白每个点的状态,当然这题直接给说了,每个点可能由左上下相邻的三个方向转移而来。
搞明白每个点状态及其状态转移,写个递归函数就能AC,还是很稳的
状态表示
ll dp[N][N][3] 对于每个点(x,y)
dp[x][y][0]代表从起点到(x,y)并且是由左侧一个点转移而来
dp[x][y][1]代表从起点到(x,y)并且是由上方一个点转移而来
dp[x][y][2]代表从起点到(x,y)并且是由下方一个点转移而来
当然这三个状态可以合并成两个。 先用三个状态方便理解。
状态转移
对于dp[x][y][0] , 显然x,y 这个点是由x,y-1 向右走一格而来,
那么dp[x][y][0] = max(dp[x][y-1][0],dp[x][y-1][1],dp[x][y-1][2]) 用到了x,y-1 的三个状态
同理,对于dp[x][y][1] , 显然x,y 这个点是由x-1,y 向下走一格而来,
那么dp[x][y][1] = max(dp[x-1][y][0],dp[x-1][y][1]) 用到了x-1,y 的两个状态
其中dp[x-1][y][2] 的这个状态对于 dp[x][y][1] 是非法的 ,因为不能先从x,y 向上到 x-1,y 又从x-1,y 向下到x,y
最后对于dp[x][y][2] , x,y 由x+1,y 向上走一格而来
dp[x][y][2] = max(dp[x+1][y][0],dp[x+1][y][2]) 用到了x+1,y 的两个状态
同理 dp[x+1][y][1]这个状态对于dp[x][y][2]是非法的
时间复杂度
由于需要计算每个点的三个状态
时间复杂度为 O(nm3) 也就是 O(nm)
初始化
由于是求最大值,先对整个dp[N][N][3] 数组赋 long long 下的极小值
注意这里不建议用memset函数
初始化起点,1,1 这个点显然不能从2,1向上走一格到达,因为起点就是1,1 一开始就被到达了
因此在dp[N][N][3]数组全为极小值的情况下
单独对 dp[1][1][0] = f[1][1] ,dp[1][1] = f[1][1];
其中f[N][N]数组是 每个点的权值
#include <bits/stdc++.h> #define ull unsigned long long #define ll long long #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f using namespace std; const int N = 1e3+10 ; const ll MIN = -LINF; int f[N][N]; ll dp[N][N][3];//由 右 上 下 转移而来 注意开long long int n,m; void init(){ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ for(int k=0;k<3;k++){ dp[i][j][k] = MIN; } } } } ll dfs(int x,int y,int k){ if(x<1 or y<1 or x>n or y>m)return MIN;//越界直接返回极小值,代表非法状态转移 if(dp[x][y][k]!=MIN)return dp[x][y][k];//!=极小值,代表被计算过,不必重复计算直接返回 if(k==0){//对于每个点的每个状态都有转移方程 dp[x][y][k] = max(dfs(x,y-1,2),max(dfs(x,y-1,0),dfs(x,y-1,1))) + (ll)f[x][y]; } else if(k==1){ dp[x][y][k] = max(dfs(x-1,y,1),dfs(x-1,y,0)) + (ll)f[x][y]; } else { dp[x][y][k] = max(dfs(x+1,y,0),dfs(x+1,y,2)) + (ll)f[x][y]; } return dp[x][y][k]; } int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&f[i][j]); } } init();//初始化 dp[1][1][0] = (ll)f[1][1],dp[1][1][1] = (ll)f[1][1]; ll ans = max(dfs(n,m,0),dfs(n,m,1)); //终点只能由左或上转移而来 printf("%lld\n",ans); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。