POJ1088 滑雪 经典DP 记忆化搜索和递推
2017-06-18 11:50
691 查看
题目链接:http://poj.org/problem?id=1088
题目内容:
Description
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
Sample Output
Source
SHTSC 2002
遇到这样的题目,先大概估算一下时间,假如用暴力搜索,对每个点都dfs一次,显然在行数和列数最大为100的情况下,必然超时。那么不妨看看用动态规划,应该怎么解决:
假设dp(i,j)以点(i,j)为起点的最长路径的长度,那么dp(i,j)等于什么呢,根据题意,某个点可以转移到另一个点,当且仅当另一个点是其上下左右的相邻点,而且高度减小。
那么可以得出dp(i,j)==max(dp(i',j')+1),(i',j')是符合题意的(i,j)的相邻点。
而答案就是所有dp(i,j)中的最大值。
1、记忆化搜索
暴力搜索的缺点在于做了大量的重复的工作,比如在求dp(0,0)的时候,假设(0,0)可以转移到(0,1),那么在求出dp(0,0)的时候,dp(0,1)必然被求了出来,当下次某个点会转移到(0,1)时,又要重新求一次dp(0,1)。
动态规划的一个思想就是,用空间换时间,既然我在转移的过程中,把某个点(i,j)的dp(i,j)给求了出来,那么为了避免下次其他点转移到这个点又要重复工作,干脆就把已经求出来的dp(i,j)的值记录下来,那么下次再遇到的时候,直接查表。这样,可以保证每个点(i,j)都只求一次dp(i,j)。
附上AC代码
那么如果用递推而不是递归,又该怎么办呢? 看下面一个例子
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=17
单调递增最长子序列
描述
求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4
输入第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000
输出输出字符串的最长递增子序列的长度
样例输入
样例输出
Longest Increasing Subsequence单调递增最长子序列,很经典的一道动态规划题目
如果用动态规划解决,那么应该很容易想到:
假设dp(i)表示以i点为终点的最长递增子序列长度,显然dp(i)=max(dp(j)+1),j<i而且j点的值比i点的值要小。
那么从哪里开始递推呢?显然dp(1)=1,第一个点作为终点,它的最长递增子序列只有它这个点。
AC代码如下:
同样的 本题和最长递增子序列是类似的,只是从一维变成了二维,那么完全可以把二维的降为一维的,降维之后,给每个点带上二维的坐标信息,就可以用上面的方法解决。
降维之后,要按点的高度给一维数组排个序,因为点在二维数组中可以向相邻点转移,你无法知道在一维数组中哪个点在哪个点的前面,一个技巧是给一维数组按点高度从小到大排序,这样后面的点一定可以由前面的点转移而来,因为前面点的高度比后面的点要低,然后判断前面的点是否是后面点的相邻点,是的话就可以转移。
AC代码如下:
题目内容:
Description
Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长底滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
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
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5 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
Sample Output
25
Source
SHTSC 2002
遇到这样的题目,先大概估算一下时间,假如用暴力搜索,对每个点都dfs一次,显然在行数和列数最大为100的情况下,必然超时。那么不妨看看用动态规划,应该怎么解决:
假设dp(i,j)以点(i,j)为起点的最长路径的长度,那么dp(i,j)等于什么呢,根据题意,某个点可以转移到另一个点,当且仅当另一个点是其上下左右的相邻点,而且高度减小。
那么可以得出dp(i,j)==max(dp(i',j')+1),(i',j')是符合题意的(i,j)的相邻点。
而答案就是所有dp(i,j)中的最大值。
1、记忆化搜索
暴力搜索的缺点在于做了大量的重复的工作,比如在求dp(0,0)的时候,假设(0,0)可以转移到(0,1),那么在求出dp(0,0)的时候,dp(0,1)必然被求了出来,当下次某个点会转移到(0,1)时,又要重新求一次dp(0,1)。
动态规划的一个思想就是,用空间换时间,既然我在转移的过程中,把某个点(i,j)的dp(i,j)给求了出来,那么为了避免下次其他点转移到这个点又要重复工作,干脆就把已经求出来的dp(i,j)的值记录下来,那么下次再遇到的时候,直接查表。这样,可以保证每个点(i,j)都只求一次dp(i,j)。
附上AC代码
#include<iostream> #include<algorithm> #include<string.h> #define width 105 using namespace std; int maps[width][width];//存地图的高度 int dp[width][width];//记录dp(i,j) int r,c;//行数和列数 int dfs(int x, int y)//深度优先搜索求解dp(x,y) { if(dp[x][y]!=0)//当dp(x,y)!=0,表示该点(x,y)的dp(x,y)已经被求过了 { return dp[x][y];//直接返回dp(x,y) } else { dp[x][y]=1;//某点(x,y)的dp(x,y)至少为1 for 4000 (int i=-1;i<=1;i++) { for(int j=-1;j<=1;j++) { if(i==0&&j!=0||i!=0&&j==0)//遍历四个相邻点 { int newx=x+i; int newy=y+j; if(newx>=0&&newx<r&&newy>=0&&newy<c)//相邻点要在地图范围内 { if(maps[newx][newy]<maps[x][y])//相邻点的高度要比该点低 { dp[x][y]=max(dfs(newx,newy)+1,dp[x][y]);//深度优先搜索该相邻点的路径长度 } } } } } return dp[x][y]; } } int main() { int ans=0; cin>>r>>c; for(int i=0;i<r;i++) { for(int j=0;j<c;j++) { cin>>maps[i][j]; } } memset(dp,0,sizeof(dp)); for(int i=0;i<r;i++) { for(int j=0;j<c;j++) { if(dp[i][j]==0)//当dp(i,j)==0,表示该点(i,j)的dp(i,j)没被计算过 { dfs(i,j);//计算该点(i,j)的dp(i,j) } if(dp[i][j]>ans) { ans=dp[i][j];//记录已经求出的所有的dp(i,j)的最大值 } } } cout<<ans<<endl; return 0; }
那么如果用递推而不是递归,又该怎么办呢? 看下面一个例子
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=17
单调递增最长子序列
描述
求一个字符串的最长递增子序列的长度
如:dabdbf最长递增子序列就是abdf,长度为4
输入第一行一个整数0<n<20,表示有n个字符串要处理
随后的n行,每行有一个字符串,该字符串的长度不会超过10000
输出输出字符串的最长递增子序列的长度
样例输入
3 aaa ababc abklmncdefg
样例输出
1 3 7
Longest Increasing Subsequence单调递增最长子序列,很经典的一道动态规划题目
如果用动态规划解决,那么应该很容易想到:
假设dp(i)表示以i点为终点的最长递增子序列长度,显然dp(i)=max(dp(j)+1),j<i而且j点的值比i点的值要小。
那么从哪里开始递推呢?显然dp(1)=1,第一个点作为终点,它的最长递增子序列只有它这个点。
AC代码如下:
#include<iostream> #include<algorithm> #include<string.h> using namespace std; int dp[10005]; int main() { int n; int ans=1; cin>>n; while(n--) { string s; cin>>s; ans=1; memset(dp,0,sizeof(dp)); dp[0]=1;//第一个点的dp值为1 for(int i=1;i<s.length();i++) { for(int j=0;j<i;j++) { if(s[i]>s[j])//j点在i点之前,而且j点的值小于i点 { dp[i]=max(dp[j]+1,dp[i]);//这就是我上面说的dp(i)=max(dp(j)+1) } if(dp[i]>ans)//用ans记录已经求出的dp(i)的最大值 { ans=dp[i]; } } } cout<<ans<<endl; } return 0; }
同样的 本题和最长递增子序列是类似的,只是从一维变成了二维,那么完全可以把二维的降为一维的,降维之后,给每个点带上二维的坐标信息,就可以用上面的方法解决。
降维之后,要按点的高度给一维数组排个序,因为点在二维数组中可以向相邻点转移,你无法知道在一维数组中哪个点在哪个点的前面,一个技巧是给一维数组按点高度从小到大排序,这样后面的点一定可以由前面的点转移而来,因为前面点的高度比后面的点要低,然后判断前面的点是否是后面点的相邻点,是的话就可以转移。
AC代码如下:
#include<iostream> #include<algorithm> #define row 105 #define length 10005 using namespace std; struct node//用一维数组去存储矩阵,带上二维数组的坐标信息 { int x; int y; int dp;//该点(x,y)的dp(x,y)的值 int value;//该点的高度 }maps[length]; bool cmp(const node& a, const node& b) { if(a.value<b.value) { return true; } else { return false; } } int main() { int r,c; int index=0; int ans=1; cin>>r>>c; for(int i=0;i<r;i++) { for(int j=0;j<c;j++) { cin>>maps[index].value; maps[index].x=i; maps[index].y=j; maps[index].dp=1;//初始的时候,每个点的最长路径dp(i,j)都为1 index++; } } sort(maps,maps+index,cmp); for(int i=1;i<index;i++) { int x=maps[i].x; int y=maps[i].y; int v=maps[i].value; for(int j=0;j<i;j++)//j点是i点前面的点 { int xx=maps[j].x; int yy=maps[j].y; int vv=maps[j].value; if(vv<v&&((abs(xx-x)==1&&yy==y)||(abs(yy-y)==1&&xx==x)))//j点的高度比i点低,而且j点是i点的相邻点,可以转移 { if(maps[j].dp+1>maps[i].dp)//相当于我说的dp(i,j)=max(dp(i',j')+1),只是这里变成一维 { maps[i].dp=maps[j].dp+1; } if(maps[i].dp>ans)//记录已经求过的dp(i)的最大值 { ans=maps[i].dp; } } } } cout<<ans<<endl; return 0; }
相关文章推荐
- POJ1088—滑雪 记忆化搜索
- poj1088--滑雪(经典dp)
- 经典dp入门---滑雪---自己看的递推,课件给的递归,先整理个递推吧--
- POJ - 1088 滑雪(在这里dp和记忆化搜索有区别吗)
- 滑雪DP;POJ1088;
- poj1088-滑雪(简单DP)
- 【DP】UVA 10651 Pebble Solitaire 记忆化搜索
- hdu2067 简单dp或者记忆化搜索
- 滑雪 (搜索)(dp)(贪心)
- NOIP模拟题 [递推][DP][搜索]
- 【DP】UVA 10651 Pebble Solitaire 记忆化搜索
- HDU 5001 概率DP || 记忆化搜索
- POJ 1088 DP 记忆化搜索
- POJ1088 滑雪 dp
- 数字三角形——递归、递推、记忆化搜索
- poj1088 滑雪 DP+DFS
- [POJ1088] 滑雪(递归dp)
- 动态规划入门(二)DP 基本思想 具体实现 经典题目 POJ1088
- [记忆化搜索DP]UVa10285 - Longest Run on a Snowboard
- 搜索——滑雪(poj1088)