忙里偷闲,继续征战动态规划。任重道远,学了那么久久懂了点皮毛。
题目描述:(好多错别字 -_-||)
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
观察是否满足 最优子结构(即是问题的最优解是否包含子问题的最优解)
很明显,在二维坐标的一点上求以此点为起始点求最长区域长度,可以将原问题变为4个子问题,即是在4个不同方向的相邻位置坐标点求原来的问题。所以满足最优子结构
关于无后效性:进行高度判断,将原点高度与其周围的位置高度比较。只有满足才进行状态转移。
为了方便描述,我们使用 height[][] 二维数组作为 位置的高度。假设于 height[i][j] 位置起始最多能滑行的区域长度为 k。很明显该题的状态就是 不同位置作为起始点最多能滑行的区域长度 ,我们使用 location(i,j) 表示。
所以该题的状态就是
location(i,j)
那么状态转移方程了,我们看看如何转移。假设该位置的上下左右4个位置都不超出边界,如果 原点位置高于这些旁边的位置高度,那么 (注意判断条件)
location(i,j)=max( location(i-1,j)+1, location(i+1,j)+1, location(i,j-1)+1,location(i,j+1)+1);
由此写出递归代码
private static int dp(int i,int j){
if(l[i][j]!=1)
return l[i][j];
if(i>0&&h[i][j]>h[i-1][j])
l[i][j]=Math.max(dp(i-1,j)+1,l[i][j] );
if(i<row-1&&h[i][j]>h[i+1][j])
l[i][j]=Math.max(dp(i+1,j)+1,l[i][j] );
if(j>0&&h[i][j]>h[i][j-1])
l[i][j]=Math.max(dp(i,j-1)+1,l[i][j] );
if(j<col-1&&h[i][j]>h[i][j+1])
l[i][j]=Math.max(dp(i,j+1)+1,l[i][j] );
return l[i][j];
}
完整代码如下
public class HuaXue {
static int row,col;
static int[][] h;
static int[][] l;
public static void main(String[] args) {
input();
calc();
}
private static void input() {
Scanner in=new Scanner(System.in);
row=in.nextInt();
col=in.nextInt();
h=new int[row][col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
h[i][j]=in.nextInt();
}
}
}
private static void calc(){
l=new int[row][col];
int max=0;
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
l[i][j]=1;
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
l[i][j]=dp(i,j);
max=Math.max(max, l[i][j]);
}
}
System.out.println("最长滑行区域长度为:"+max);
}
private static int dp(int i,int j){
if(l[i][j]!=1)
return l[i][j];
if(i>0&&h[i][j]>h[i-1][j])
l[i][j]=Math.max(dp(i-1,j)+1,l[i][j] );
if(i<row-1&&h[i][j]>h[i+1][j])
l[i][j]=Math.max(dp(i+1,j)+1,l[i][j] );
if(j>0&&h[i][j]>h[i][j-1])
l[i][j]=Math.max(dp(i,j-1)+1,l[i][j] );
if(j<col-1&&h[i][j]>h[i][j+1])
l[i][j]=Math.max(dp(i,j+1)+1,l[i][j] );
return l[i][j];
}
}
下面是优化版本
package com.luo.daily;
import java.util.Scanner;
//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更长。事实上,这是最长的一条。
public class HuaXue {
static int row,col;
static int[][] input;
static int[][] temp;
public static void main(String[] args) {
input();
calc();
}
private static void input() {
Scanner in=new Scanner(System.in);
row=in.nextInt();
col=in.nextInt();
input=new int[row][col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
input[i][j]=in.nextInt();
}
}
}
private static void calc(){
temp=new int[row][col];
int max=0;
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
max=Math.max(max, dp(i,j));
}
}
System.out.println("最长区域长度是:"+max);
}
static int dp(int i,int j){
if(temp[i][j]!=0)
return temp[i][j];
int maxn=0;
if(i-1>=0&&input[i][j]>input[i-1][j]&&dp(i-1,j)>maxn)
maxn=temp[i-1][j];
if(i+1<row&&input[i][j]>input[i+1][j]&&dp(i+1,j)>maxn)
maxn=temp[i+1][j];
if(j-1>=0&&input[i][j]>input[i][j-1]&&dp(i,j-1)>maxn)
maxn=temp[i][j-1];
if(j+1<col&&input[i][j]>input[i][j+1]&&dp(i,j+1)>maxn)
maxn=temp[i][j+1];
temp[i][j]=maxn+1;
return temp[i][j];
}
}