POJ1088 滑雪【动态规划】

题目:

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

思路:

一开始我是以为,山峰肯定只有一个峰,那这样的话,找到最高的那个点,在它周围慢慢找高度减小的点就好了。后来发现我太naive了,下面就是其中一组测试数据。

12 13
1 1 30 4 800 6 7 8 99 10 1223 1
20 30 30 4 16 15 14 13 12 11 1
21 22 99 444444 88 9926 27 9928 9929 3000 456 1
40 39 1 90 36 35 34 33 3992 30001 789 1
41 42 4000 44 88 46 47 48 49 50 897 1
1 59 1 57 56 85 54 53 52 51 908 1
61 77 56 64 444 66 67 68 69 70 1234 1
80 79 78 77 76 75 74 73 72 71 12345 1
81 82 2 2 4 86 5 88 8 90 3456 1
100 99 98 97 96 95 94 93 92 91 567 1
890 654 623 154 683 15414 86549 633 123 456 123456 1
9517 45632 643164 3478643 43 16 431 64453132 689431 746546 15643 1
64543 13146543 13474 314789 4352154 65431 631 654324 65132 89547 34567312 1 1

答案是27

所以不能想得太简单。正确的思路应该是这样的:题意为,给出一个二维数组,让你求出最长递减序列长度,可以四个方向行走,起点任意。要对于每个点,都算出到达1的最长路径。

如果单纯的递归,会超时,时间限制只有1000ms,所以加点小技巧,每次第一次访问一个点,就记录它到达1的最长路径,当下次访问时,就直接返回记录的值。即用一个数组dis[101][101]存储每个节点的最长路径,先初始化为0,每当访问一个节点时,就判断dis值是否大于0,大于0则此点的值就是dis的值,直接返回。

代码:

#include<iostream>
using namespace std;
int map[101][101]={0};//存原始数据
int r,c;//行数,列数
int dis[101][101]={0};//存储节点的最长路劲值

int f(int form,int to){
	if(dis[form][to]>0)return dis[form][to];//如果已经记录了值,直接返回
	int zuo=0,you=0,shang=0,xia=0;
	if(form+1<=r){//处理上路
		if(map[form+1][to]<map[form][to])shang=1+f(form+1,to);
		else shang=1;
	}
	else shang=1;
	if(form-1>=1){//处理下路
		if(map[form-1][to]<map[form][to])xia=1+f(form-1,to);
		else xia=1;
	}
	else xia=1;
	if(to+1<=c){//处理右路
		if(map[form][to+1]<map[form][to])you=1+f(form,to+1);
		else you=1;
	}
	else you=1;
	if(to-1>=1){//处理左路
		if(map[form][to-1]<map[form][to])zuo=1+f(form,to-1);
		else zuo=1;
	}
	else zuo=1;
	if(zuo<you)zuo=you;
	if(zuo<shang)zuo=shang;
	if(zuo<xia)zuo=xia;
	return zuo;//返回最大值
}

int main(){
	int i,j;
	cin>>r>>c;
	for(i=1;i<=r;i++)
		for(j=1;j<=c;j++)
			cin>>map[i][j];
	int sun=0;
	for(i=1;i<=r;i++)
		for(j=1;j<=c;j++){
			dis[i][j]=f(i,j);//记录每个节点的最长路劲
			if(sun<dis[i][j])sun=dis[i][j];//选出最大长度
		}
	cout<<sun<<endl;
	return 0;
}
/*
测试数据
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


1 1
1

1 2
1 2

1 3 
1 3 2

2 2
1 2 
4 3

2 2 
1 2 
3 4

4 7
7 6 5 4 3 2 1
1 5 1 1 1 1 1
1 4 3 1 1 1 1
1 5 6 7 8 1 1

3 3
9 1 2
5 6 7
8 4 3

3 4
1 2 3 4
8 7 6 5
9 10 11 12

3 3
0 0 0
0 5 0
0 0 0

12 13
1   1 30  4  800  6  7  8  99 10 1223 1
20 30 30 4 16 15 14 13 12 11 1
21 22 99 444444 88 9926 27 9928 9929 3000 456 1
40 39 1 90 36 35 34 33 3992 30001 789  1
41 42 4000 44  88 46 47 48 49 50 897  1
1 59 1 57 56  85 54 53 52 51 908 1
61 77 56 64 444 66 67 68 69 70 1234 1
80 79 78 77 76 75 74 73 72 71 12345 1
81 82 2  2 4 86 5 88 8 90 3456  1
100 99 98 97 96 95 94 93 92 91 567 1
890 654 623 154 683 15414 86549 633 123 456 123456  1
9517 45632 643164 3478643 43 16 431 64453132 689431 746546 15643 1
64543 13146543 13474 314789 4352154 65431 631 654324 65132 89547  34567312 1 1


13 12
1   1 30  4  800  6  7  8  99 10 1223 1
20 30 30 4 16 15 14 13 12 11 1
21 22 99 444444 88 9926 27 9928 9929 3000 456 1
40 39 1 90 36 35 34 33 3992 30001 789  1
41 42 4000 44  88 46 47 48 49 50 897  1
1 59 1 57 56  85 54 53 52 51 908 1
61 77 56 64 444 66 67 68 69 70 1234 1
80 79 78 77 76 75 74 73 72 71 12345 1
81 82 2  2 4 86 5 88 8 90 3456  1
100 99 98 97 96 95 94 93 92 91 567 1
890 654 623 154 683 15414 86549 633 123 456 123456  1
9517 45632 643164 3478643 43 16 431 64453132 689431 746546 15643 1
64543 13146543 13474 314789 4352154 65431 631 654324 65132 89547  34567312 1 1

3 3
0 1 2
1 0 1
2 1 0

3 3
0 0 0
0 0 0
0 0 0

1 1
0

10 10
1 2 300 4 5 6 7 8 9 10
20 19 18 17 16 15 14 13 12 11
21 22 23 24 25 26 27 28 29 30
40 39 38 37 36 35 34 33 32 31
41 42 43 44 45 46 47 48 49 50
60 59 58 57 56 55 54 53 52 51
61 62 63 64 65 66 67 68 69 70
80 79 78 77 76 75 74 73 72 71
81 82 83 84 85 86 87 88 89 90
100 99 98 97 96 95 94 93 92 91

4 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4

4 4
1 2 2 1
1 4 4 1
1 3 3 1
1 2 2 1

3 3
9 1 2
5 6 7
8 4 3

Sample Output

25
1
2
2
4
3
7
4
12
2
27
37
3
1
1
97
4
4
4
*/

这上面是上下左右一个个判断的,我后来翻别人的代码时,发现他们用[4][2]数组来表示方向,到时候用(for int i=0;i<4;i++)来遍历4个方向。也挺好的。

//循环出四个方面的最长序列然后len[i][j]=max(len[上],下,左,右)+1;
#include <iostream>
#define N 101
using namespace std;
int map[N][N],len[N][N];
int dir[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
int r,c;
int dp(int i,int j){
    if(len[i][j]!=0)return len[i][j];
    int maxx=0,s;
    for(int t=0;t<4;t++){
          int temx=i+dir[t][0],temy=j+dir[t][1];
          if(temx>=0&&temx<r&&temy>=0&&temy<c&&map[temx][temy]<map[i][j]){
              s=dp(temx,temy);
              if(s>maxx)maxx=s;
          }
           
      }
    len[i][j]=maxx+1;
    return maxx+1;
}
 
int main(){
		cin>>r>>c;
        int mx=-1;
        memset(len,0,sizeof(len));
        for(int i=0;i<r;i++){
           for(int j=0;j<c;j++){
			   cin>>map[i][j];
		   }
		}
        for(int i=0;i<r;i++){
           for(int j=0;j<c;j++){
			   len[i][j]=dp(i,j);
			   if(len[i][j]>mx){
			      mx=len[i][j]; 
			   }
		   }         
        }
		cout<<mx;
	    return 0;
}

    原文作者:动态规划
    原文地址: https://blog.csdn.net/huanghanqian/article/details/51646241
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞