您的位置:首页 > 其它

动态规划5--滑雪

2017-06-09 10:48 155 查看

动态规划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 }


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: