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