最近忙于工程项目设计,算法落下了一点,碰巧近期在学动态规划算法,很有用,同时有很具有思考性,还是重新回顾了动规和记忆化搜索的经典题目Longest Run on a Snowboard(有的oj又名曰Skiing),
题面
大致的题意就是在一个二维地图中找出一条最长严格下降路径,很显然,这就是一道搜索算法题。
思路启示
想要在这么一个二维地图中找到所需路径,单从一个点出发肯定是不够的,必须尝试从二维地图中的每个点出发,dfs到一条最长路径,每个点遍历过程中更新路径的最大值。然而,要是真的只是这么做的话,就很愉快地TLE了。为什么呢?
核心——记忆化搜索
问题很明显,对同一个格点,有可能进行了重复性的工作(可以自己模拟一下,就知道哪儿重复了)。对于一个格点,其路径最大值是固定的,一旦确定下来,以后就不会改变,需要用到的时候直接拿过来用(有点像…动归中的无后效性??暂且可以这么理解)。这么一来,开一个dp数组来记录其值不就能够做到“剪枝”了?
动态规划思想
在思考动态规划问题的时候,我们的思维不是跳跃的,而是着眼当下,重点思考当前问题与子问题的关系。拿本题来说,对于格点(x,y),其最长路径的来源无非就是上下左右4个方向加1。把当前问题想好了,由于问题具有最有子结构(重叠子问题)的性质,接下去的解法与当前都是一样的,只是规模变小。
C++代码实现(55行)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define N 102
using namespace std;
int n,m;
int a[N][N];
int dp[N][N];
int nxt[][2] = {{-1,0}, {0,1}, {1,0}, {0,-1} };
inline bool check(int x, int y) {
return x >= 1 && y >= 1 && x <= n && y <= m;
}
int dfs(int x, int y) {
int tmp = 0;
for (int i = 0; i < 4; i++) {
int tx = x + nxt[i][0];
int ty = y + nxt[i][1];
if (check(tx,ty) && a[tx][ty] < a[x][y] ) {
if (dp[tx][ty] != -1) {
tmp = max(dp[tx][ty]+1,tmp);
} else {
int c = dfs(tx,ty);
tmp = max(c+1,tmp);
dp[tx][ty] = c;
}
}
}
return dp[x][y] = tmp;
}
int main() {
int t; cin >>t;
while(t--) {
memset(dp,-1,sizeof(dp));
string name;
cin >> name;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d",&a[i][j]);
}
}
int ans = -1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
ans = max(ans,dfs(i,j));
}
}
cout << name << ":" << " " << ans+1 << "\n";
}
return 0;
}
后记
如果您对算法与数据结构感兴趣,欢迎关注我的公众小号(ACMFans Club)和我交流,公众号上面也放了一些我的个人小项目(虽然目前不会很多),涵盖Java网络编程,Python爬虫,Qt编程,Web前端…
鉴于经典题目的流传性,以后我会在公众号更新的时候同时更新我的CSDN博客。
时隔2个月,再度回归算法,谨此。