P1434 [SHOI2002]滑雪 记忆化搜索,深度搜索,动态规划
第一篇题解,实际上是几天前做的题目
1 2 3 4 5 16 17 18 19 6 15 24 25 20 7 14 23 22 21 8 13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度会减小。在上面的例子中,一条可行的滑坡为 2424-1717-1616-11(从 2424 开始,在 11 结束)。当然 2525-2424-2323-\ldots…-33-22-11 更长。事实上,这是最长的一条。
输出区域中最长滑坡的长度。
深度搜索dsf(int x, int y)返回从(x,y)滑下的最长路径,对每个点调用该函数后取最大值即为答案.
搜索路径较多,使用数组 dp[x][y]记录调用dfs(x, y)的返回值以加速.(实际上这题也有dp的状态转移的思想)
对于每次(每个点)的搜索,检查其上下左右四个相邻位置(nx,ny)对于所有高度小于当前位置的相邻位置,取dfs(nx,ny)返回值最大者,则当前位置的返回值为该最大值与其原本值(若有)的最大者+1.若所有相邻位置高度均大于当前位置(bad),则该点的返回值为1.
细节:1.对于存储高度的数组,从(0,0)开始直到(r+1,c+1)均初始化为INF,而读入数据存储到从(1,1)开始直到(r,c)的位置,使得INF形成包围之势防止滑雪滑出场地.
# # # # # # # # 1 2 3 4 5 # # 16 17 18 19 6 # # 15 24 25 20 7 # # 14 23 22 21 8 # # 13 12 11 10 9 # # # # # # # # (#指INF)
2.数组dp初始化为-1.
可以证明,第一次调用dfs会在某个高度低于所有相邻位置的点处返回1并在此处终止一次递归,所有的递归总会达到正确的终止条件,虽然直觉让人觉得不停地对陌生的点调用dfs会陷入麻烦.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define INF 100000000 int s[110][110], dp[110][110]; int r, c, dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0}; int dfs(int x, int y) { bool bad = true; if(dp[x][y] > 0) return dp[x][y]; else for(int i = 0; i < 4; i++) { int nx = x + dx[i], ny = y + dy[i]; if(s[nx][ny] < s[x][y]) { dp[x][y] = max(dfs(nx, ny), dp[x][y]); bad = false; } } if(bad) return dp[x][y] = 1; //end else return ++dp[x][y]; } int main() { cin >> r >> c; for(int i = 0; i <= r + 1; i++) for(int j = 0; j <= c + 1; j++) s[i][j] = INF; for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) cin >> s[i][j]; memset(dp, -1, sizeof(dp)); for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) dfs(i, j); int ans = -1; for(int i = 1; i <= r; i++) for(int j = 1; j <= c; j++) ans = max(ans, dp[i][j]); printf("%d\n", ans); return 0; }