动态规划5--滑雪

一、心得

找路径时,递推的方法和递归一样,也是知道递推表达式之后就特别好写了

也是直接把递推表达式写进循环里面就好了

递推和递推写法的区别:

递归是调用的系统栈,递推没有调用栈,其它一模一样了

 

二、题目和分析

滑雪:

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更长。事实上,这是最长的一条。输入输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。输出输出最长区域的长度。

输入

输入的第一行表示区域的行数R和列数C

(1 <= R,C <= 100)。下面是R行,每行有C个整数,

代表高度h,0<=h<=10000。

输出

输出最长区域的长度。

样例输入

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

样例输出

25

 

先定义结构体把每个点的x,y和h都装起来,依次储存每个点。

那这个问题就从二维化成了一维。

按每个点高度从大到小排好。

其实就变成了不下降子序列那个问题。

具体做法:

我按高度依次往后面找,看看后面的点是否满足点在这个点旁边的规则。

满足的话就路径长度加1。

 

L(i,j)表示从点(i,j)出发的最长滑行长度。

一个点(i,j), 如果周围没有比它低的点,L(i,j) = 1

否则

递推公式: L(i,j) 等于(i,j)周围四个点中,比(i,j)低,且L值最大的那个点的L值,再加1

复杂度:O(n2)

解法1) “人人为我”式递推

L(i,j)表示从点(i,j)出发的最长滑行长度。

一个点(i,j), 如果周围没有比它低的点,L(i,j) = 1

将所有点按高度从小到大排序。每个点的 L 值都初始化为1

从小到大遍历所有的点。经过一个点(i,j)时,用递推公式求L(i,j)

 

解法2) “我为人人”式递推

L(i,j)表示从点(i,j)出发的最长滑行长度。

一个点(i,j), 如果周围没有比它低的点,L(i,j) = 1

将所有点按高度从小到大排序。每个点的 L 值都初始化为1

从小到大遍历所有的点。经过一个点(i,j)时,要更新他周围的,比它高的点的L值。例如:

if H(i+1,j) > H(i,j) // H代表高度

L(i+1,j) = max(L(i+1,j),L(i,j)+1)

三、代码和结果



1 /*
2 心得:
3 找路径时,递推的方法和递归一样,也是知道递推表达式之后就特别好写了
4 也是直接把递推表达式写进循环里面就好了
5 递推和递推写法的区别:
6 递归是调用的系统栈,递推没有调用栈,其它一模一样了
7
8 将题目转化为了求不下降子序列的长度
9 */
10 #include <iostream>
11 #include <algorithm>
12 #define Max 105
13 using namespace std;
14
15 struct node{//表示每一个点
16 int r;//每一个点的行位置
17 int c;//每一个点的列位置
18 int h;//每一个点的高
19 };
20 //排序规则,将结构从大到小排序
21 int my_comp(const node &p1,const node &p2){
22 return p1.h>p2.h;
23 //本来顺序是p1,p2,如果p1的高大于p2的高,我们就不交换
24 }
25 node a[Max*Max];//储存每一个节点,按高度排序后将二维数组化成了一维数组
26 //dp[i]表示第i个点对应的最长区域的长度
27 int R,C,dp[Max*Max];//R表示行,R表示列
28 int pre[Max*Max];//记录每个点的路径
29 int maxn=0,maxn_i=R*C;//记录最长路径对应的点的值和编号
30
31 void input();//输入数据
32 void test_input(int R,int C);//测试输入数据
33 void init_dp();//动态规划前的初始化,初始化dp数组
34 void dpFunction();//dp操作
35 bool isClose(node a,node b);//判断两个点是否相连
36 void printRoad1(int i);//递归方法打印路径
37 void printRoad2(int i);//递推方法打印路径
38
39
40
41
42 int main(){
43 freopen("in.txt","r",stdin);
44
45 input();//输入数据
46
47 sort(a+1,a+R*C+1,my_comp);//对每个点按高度进行排序
48 //test_input(R,C); //测试输入数据
49 init_dp();//动态规划前的初始化,初始化dp数组
50 dpFunction();//dp操作
51 //递归找路径
52 printRoad1(pre[maxn_i]);
53 cout<<a[maxn_i].r<<","<<a[maxn_i].c<<endl;//输出第一个点
54
55 //递推找路径
56 //cout<<a[maxn_i].r<<","<<a[maxn_i].c;//输出第一个点
57 //printRoad2(pre[maxn_i]);//输出后面的点
58 return 0;
59 }
60
61 //递推方法打印路径
62 void printRoad2(int i){
63 /*
64 递推表达式是:
65 如果pre[i]==0,表示是起始点,return
66 如果不是0,递归呗
67 */
68 /*
69 找路径时,递推的方法和递归一样,也是知道递推表达式之后就特别好写了
70 也是直接把递推表达式写进循环里面就好了
71 递推和递推写法的区别:
72 递归是调用的系统栈,递推没有调用栈,其它一模一样了
73 */
74 while(true){
75 if(i==0){
76 break;
77 }
78 else{
79 cout<<"-->"<<a[i].r<<","<<a[i].c;
80 i=pre[i];
81 }
82 }
83 }
84
85 //递归方法打印路径
86 void printRoad1(int i){
87 /*
88 递推表达式是:
89 如果pre[i]==0,表示是起始点,return
90 如果不是0,递归呗
91 */
92 if(i==0){
93 return ;
94 }
95 else{
96 printRoad1(pre[i]);
97 cout<<a[i].r<<","<<a[i].c<<"-->";
98 }
99 }
100
101 //dp操作
102 void dpFunction(){
103 //从高往低走,如果靠近,上下左右相邻四个点之一,最长区域就加1
104 for(int i=1;i<=R*C;i++){
105 for(int j=1;j<i;j++){
106 if(isClose(a[i],a[j])){
107 if(dp[j]+1>=dp[i]){
108 dp[i]=dp[j]+1;
109 pre[i]=j;
110 }
111 }
112 }
113 }
114 //路径最长的点并不是一定从最后面出来的那个点,所以cout<<dp[R*C]<<endl;是不对的
115 //还要找到dp里面那个最大的值,和那个值对应的编号
116
117 for(int i=1;i<=R*C;i++){
118 if(dp[i]>maxn){
119 maxn=dp[i];
120 maxn_i=i;
121 }
122 }
123 //cout<<maxn<<" "<<maxn_i<<endl;
124 cout<<maxn<<endl;
125
126 }
127
128 //判断两个点是否相连
129 bool isClose(node a,node b){
130 /*
131 r-1
132 c-1 r,c c+1
133 r+1
134 */
135 //对应上下左右,
136 //r在前列子在后
137 int direction[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
138 // cout<<direction[0][0]<<endl;
139 // cout<<direction[0][1]<<endl;
140 // cout<<direction[1][0]<<endl;
141 // cout<<direction[1][1]<<endl;
142
143 for(int i=0;i<4;i++){
144 //a在中心
145 if((a.r+direction[i][0]==b.r)&&(a.c+direction[i][1]==b.c)){
146 return true;
147 }
148 }
149 return false;
150
151 }
152
153 //动态规划前的初始化,初始化dp数组
154 void init_dp(){
155 for(int i=1;i<=R*C;i++){
156 dp[i]=1;
157 }
158 }
159
160 //输入数据
161 void input(){
162 cin>>R>>C;
163 int num=1;
164 for(int i=1;i<=R;i++){
165 for(int j=1;j<=C;j++){
166 a[num].r=i;
167 a[num].c=j;
168 cin>>a[num++].h;
169 }
170 }
171 }
172
173 //测试输入数据
174 void test_input(int R,int C){
175 for(int i=1;i<=R*C;i++){
176 cout<<a[i].r<<" "<<a[i].c<<" "<<a[i].h<<endl;
177 }
178 }


动态规划5--滑雪_递归


我的旨在学过的东西不再忘记(主要使用艾宾浩斯遗忘曲线算法及其它智能学习复习算法)的偏公益性质的完全免费的编程视频学习网站: ​​fanrenyi.com​​;有各种前端、后端、算法、大数据、人工智能等课程。

​版权申明:欢迎转载,但请注明出处​

一些博文中有一些参考内容因时间久远找不到来源了没有注明,如果侵权请联系我删除。

博主25岁,前端后端算法大数据人工智能都有兴趣。