内容二
Michael 喜欢滑雪。为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底, 不得不再次走上坡或者等付升降机来载你。
区域由一个二维数组给出。数组的每个数字代表点的高度。
当且仅当高度减小,一个人可以从某个点滑向上下 左右相邻四个点之一。
求:一个区域中最长的滑坡。
请设计算法求解以上问题并做分析
转移方程:
dp[ i ][ j ] =max(dp[ i-1 ][ j ],dp[ i+1 ][ j ],dp[ i ][ j-1 ],dp[ i ][j+1])+1;
拓展:记忆化搜索
分析:比下面那道题复杂一点,复杂的点在于下面那道题是从某一固定点到另外一个固定点,这道题是计算任意一点到某一未知点的最大距离。所以相当于是遍历了滑雪场所有的点(每一个点都有可能是起点),然后对于每一个点都深搜用动态规划的办法求出最大值。
import java.util.Scanner;
/**
* @author SJ
* @date 2020/10/27
*/
public class GoSkating {
public static int[][] ground;//滑雪场
public static int X;//行
public static int Y;//列
public GoSkating(int x,int y) {
X=x;
Y=y;
ground=new int[X][Y];
dp=new int[X][Y];
}
public static void inputGround(){
System.out.println("请输入二维数组");
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < X; i++) {
for (int i1 = 0; i1 < Y; i1++) {
ground[i][i1]=scanner.nextInt();
}
}
}
public static int[][] dp;//最优解
//测试输出
public static void printDp(){
for (int i = 0; i < dp.length; i++) {
for (int i1 = 0; i1 < dp[i].length; i1++) {
System.out.print(dp[i][i1]+" ");
}
System.out.println();
}
}
//判断前后左右是不是比他大
public static boolean isGradient(int x,int y,String s){
switch (s){
//左边是否比它高
case "left":
if (x-1>=0&&ground[x-1][y]>ground[x][y])
return true;
break;
//右边是否比它高
case "right":
if (x+1<Y&&ground[x+1][y]>ground[x][y])
return true;
break;
//上边是否比它低
case "up":
if (y-1>=0&&ground[x][y-1]>ground[x][y])
return true;
break;
//下边是否比它低
case "down":
if (y+1<X&&ground[x][y+1]>ground[x][y])
return true;
break;
}
return false;
}
public static int dfs(int x,int y){
int res=1;
if (dp[x][y]!=0)
return dp[x][y];
if (isGradient(x,y,"left")){
res=Math.max(res,dfs(x-1,y)+1);
}
if (isGradient(x,y,"right"))
res=Math.max(res,dfs(x+1,y)+1);
if (isGradient(x,y,"up"))
res=Math.max(res,dfs(x,y-1)+1);
if (isGradient(x,y,"down"))
res=Math.max(res,dfs(x,y+1)+1);
dp[x][y]=res;
return dp[x][y];
}
public static void main(String[] args) {
int x=5;
int y=5;
new GoSkating(x,y);
inputGround();
int res=-1;
for (int i = 0; i < X; i++) {
for (int j = 0; j < Y; j++) {
res=Math.max(res,dfs(i,j));
}
}
System.out.println("最大长度为:"+res);
// printDp();
}
}
测试:
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"
请输入二维数组
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
Process finished with exit code 0
对每个点进行深搜,遇到四面都是较大值时或者遇到dp中已经有过记录的点开始回朔。直至搜索完最后一个点。因为每个点只计算了一次,自然,这里的时间复杂度仅有O(X*Y)。
内容三
给定一个𝑛 ∗ 𝑚的矩阵,每个单元中都有 一个非负整数,只能向右或向下移动
求从左上角到右下角的所有路径中的最大 值(每条路径的值为对路径中所进过的格子中的数求和)
请设计算法求解以上问题并做分析
动态转移方程:
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + nums[i][j];
本题有点简单的,没啥好讲思路的。
import java.util.Scanner;
/**
* @author SJ
* @date 2020/10/21
*/
public class PickUpApples {
public static int[][] nums;
public static int X;//行
public static int Y;//列
public static void inputNums() {
System.out.println("请输入二维数组");
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < X; i++) {
for (int i1 = 0; i1 < Y; i1++) {
nums[i][i1] = scanner.nextInt();
}
}
}
public PickUpApples(int x,int y) {
X=x;
Y=y;
nums=new int[X][Y];
}
public static int pickMax() {
int[][] dp = new int[X][Y];
dp[0][0] = nums[0][0];
int max=dp[0][0];
//初始化第一排和第一列
for (int i = 1; i < X; i++) {
dp[0][i] = dp[0][i - 1] + nums[0][i];
max=Math.max(max,dp[0][i]);
}
for (int i = 1; i < Y; i++) {
dp[i][0] = dp[i - 1][0] + nums[i][0];
max=Math.max(max,dp[i][0]);
}
//转移方程
for (int i = 1; i < X; i++) {
for (int j = 1; j < Y; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + nums[i][j];
max=Math.max(max,dp[i][j]);
}
}
// for (int[] ints : dp) {
// System.out.println(Arrays.toString(ints));
// }
return max;
}
public static void main(String[] args) {
int x=3;
int y=3;
new PickUpApples(3,3);
inputNums();
int i = pickMax();
System.out.println(i);
}
}
结果:
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"...
请输入二维数组
1 2 3
4 5 6
7 8 9
左上到右下的最大值:29
Process finished with exit code 0
时间复杂度
内容四
给定一个𝑛 ∗ 𝑚的矩阵,每个单元都有一 个值,现在从左上走到右下再回到左上, 中间不走重复的点,求所走单元的和的最 大值。
请设计算法求解以上问题并做分析。
分析:看成两个点(i,j)和 (k,d)同时从左上走到右下,保证中途不相交即可
这次采用备忘录方法(dp也能做,思想差不多)。
从右下开始一层层向左上递归。
而这里的dp是同时对两条不相交的路进行。于是memo数组就用四维的,表示同时走到和时的最大值,而走到每个点有左上两条路,两个点,则有四种组合情况
状态转移方程
memo[i][j][k][d]=max(max(memo[i-1][j][k-1][d],memo[i-1][j][k][d-1]),max(memo[i][j-1][k-1][d],memo[i][j-1][k][d-1]))+nums[i][j]+nums[k][d];
import java.util.Scanner;
/**
* @author SJ
* @date 2020/10/27
*/
public class FindMaxRoute {
public static int[][] nums;
public static int X;//行
public static int Y;//列
public static int[][][][] memo;
public static void inputNums() {
System.out.println("请输入二维数组");
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < X; i++) {
for (int i1 = 0; i1 < Y; i1++) {
nums[i][i1] = scanner.nextInt();
}
}
}
public FindMaxRoute(int x, int y) {
X = x;
Y = y;
nums = new int[X][Y];
memo = new int[X][Y][X][Y];
}
//用备忘录解法
public static int dfs(int i, int j, int k, int d) {
//两个点(i,j)和(k,d)同时从右下下往右上角走
if (memo[i][j][k][d] != 0)
return memo[i][j][k][d];
//递归触底
if (i == 0 && j == 0 && k == 0 && d == 0)
memo[i][j][k][d] = nums[0][0] * 2;
else if (i == k && j == d && !(i == X - 1 && j==Y-1))//两点相交
{
return 0;
} else //两个点四种可能
{
int temp1 = -1;
//都向左
if (i - 1 >= 0 && k - 1 >= 0)
temp1 = Math.max(dfs(i - 1, j, k - 1, d) + nums[i - 1][j] + nums[k - 1][d], temp1);
//左边的向左,右边上向上
if (i - 1 >= 0 && d - 1 >= 0)
temp1 = Math.max(dfs(i - 1, j, k, d - 1) + nums[i - 1][j] + nums[k][d - 1], temp1);
//左边的向上,右边的向左
if (j - 1 >= 0 && k - 1 >= 0)
temp1 = Math.max(dfs(i, j - 1, k - 1, d) + nums[i][j - 1] + nums[k - 1][d], temp1);
//都向上
if (j - 1 >= 0 && d - 1 >= 0)
temp1 = Math.max(dfs(i, j - 1, k, d - 1) + nums[i][j - 1] + nums[k][d - 1], temp1);
memo[i][j][k][d] = temp1;
}
return memo[i][j][k][d];
}
//测试输出
public static void printMemo() {
for (int i = 0; i < X; i++) {
for (int j = 0; j < Y; j++) {
for (int k = 0; k < X; k++) {
for (int l = 0; l < Y; l++) {
System.out.print(memo[i][j][k][l]);
}
System.out.println();
}
System.out.println();
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) {
int x = 3;
int y = 3;
new FindMaxRoute(3, 3);
inputNums();
int dfs = dfs(2, 2, 2, 2);
System.out.println("最大值为:"+dfs);
//printMemo();
}
}
测试结果:
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe" ...
请输入二维数组
0 2 9
4 8 6
2 7 0
最大值为:36
Process finished with exit code 0
因为是四维数组,不管是动态规划还是备忘录,时间复杂度是,备忘录用的还是递归,需要一层层回溯所以花的时间会多一些。