动态规划5--滑雪

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

原文地址:https://www.cnblogs.com/Renyi-Fan/p/6970882.html