题目描述
解题思路
这是一道「memoization(记忆化)」的经典题目,memoization其实就是“带备忘录的递归”,将之前已经计算过的问题的答案存储起来,下一次再遇到时就不用计算,可以直接返回结果。
定义状态dp[i][j]的含义为:以map[i][j]开始的最长递减路线的长度。由此可得下面的状态转移关系:
d
p
[
i
]
[
j
]
=
m
a
x
{
从
m
a
p
[
i
]
[
j
]
重新开始:
1
如果上面还有位置,从
m
a
p
[
i
−
1
]
[
j
]
向下走一步:
d
p
[
i
−
1
]
[
j
]
+
1
如果左面还有位置,从
m
a
p
[
i
]
[
j
−
1
]
向右走一步:
d
p
[
i
]
[
j
−
1
]
+
1
如果下面还有位置,从
m
a
p
[
i
+
1
]
[
j
]
向上走一步:
d
p
[
i
+
1
]
[
j
]
+
1
如果右面还有位置,从
m
a
p
[
i
]
[
j
+
1
]
向左走一步:
d
p
[
i
]
[
j
+
1
]
+
1
dp[i][j] = max \left\{\begin{matrix} \begin{aligned} &从map[i][j]重新开始:&1 \\ &如果上面还有位置,从map[i-1][j]向下走一步:&dp[i-1][j]+1\\ &如果左面还有位置,从map[i][j-1]向右走一步:&dp[i][j-1]+1\\ &如果下面还有位置,从map[i+1][j]向上走一步:&dp[i+1][j]+1\\ &如果右面还有位置,从map[i][j+1]向左走一步:&dp[i][j+1]+1\\ \end{aligned} \end{matrix}\right.
dp[i][j]=max⎩
⎨
⎧从map[i][j]重新开始:如果上面还有位置,从map[i−1][j]向下走一步:如果左面还有位置,从map[i][j−1]向右走一步:如果下面还有位置,从map[i+1][j]向上走一步:如果右面还有位置,从map[i][j+1]向左走一步:1dp[i−1][j]+1dp[i][j−1]+1dp[i+1][j]+1dp[i][j+1]+1
计算出所有的dp[i][j]后,从中选出最大值即为所求结果。
int dp[100][100]; // dp[i][j]: 以map[i][j]开始的最长递减路线的长度
...
int solve(){
// 初始化:dp[i][j] = -1 =>尚未计算出来
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
dp[i][j] = -1;
}
}
// 计算出所有的dp[i][j]后,并从中选出最大值
int max_length = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
dp[i][j] = longest_path_desc(i, j);
max_length = max(dp[i][j], max_length);
}
}
return max_length;
}
...
// 带备忘录的递归
int longest_path_desc(int i, int j){
if(dp[i][j] > 0)
return dp[i][j]; // 已经计算过了,直接返回结果
// 没有计算过,按照原本的递归逻辑进行计算
int length = 1;
if(i-1>=0 && map[i-1][j] < map[i][j]){
length = max(length, longest_path_desc(i-1,j)+1);
}
if(j-1>=0 && map[i][j-1] < map[i][j]){
length = max(length, longest_path_desc(i,j-1)+1);
}
if(i+1<n && map[i+1][j] < map[i][j]){
length = max(length, longest_path_desc(i+1,j)+1);
}
if(j+1<m && map[i][j+1] < map[i][j]){
length = max(length, longest_path_desc(i,j+1)+1);
}
return length;
}
C++代码实现
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
int n, m;
int map[100][100];
void input(){
cin >> n >> m;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cin >> map[i][j];
}
}
}
int dp[100][100]; // dp[i][j]: 以map[i][j]开始的最长递减路线的长度
// 返回最长
int longest_path_desc(int i, int j){
if(dp[i][j] > 0)
return dp[i][j]; // 已经计算过了,直接返回结果
// 没有计算过,按照原本的递归逻辑进行计算
int length = 1;
if(i-1>=0 && map[i-1][j] < map[i][j]){
length = max(length, longest_path_desc(i-1,j)+1);
}
if(j-1>=0 && map[i][j-1] < map[i][j]){
length = max(length, longest_path_desc(i,j-1)+1);
}
if(i+1<n && map[i+1][j] < map[i][j]){
length = max(length, longest_path_desc(i+1,j)+1);
}
if(j+1<m && map[i][j+1] < map[i][j]){
length = max(length, longest_path_desc(i,j+1)+1);
}
return length;
}
int solve(){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
dp[i][j] = -1;
}
}
int max_length = 0;
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
dp[i][j] = longest_path_desc(i, j);
max_length = max(dp[i][j], max_length);
}
}
return max_length;
}
int main() {
// freopen("input.txt", "r", stdin);
input();
cout << solve();
return 0;
}
// 64 位输出请用 printf("%lld")