算法拾遗二十三之暴力递归到动态规划一
创始人
2024-05-05 13:48:33
0

算法拾遗二十三之暴力递归到动态规划一

      • 题目一
        • 优化Code(空间换时间)
          • 优化二
      • 题目二
        • 优化一(缓存法)
        • 优化三(严格表优化)

题目一

假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2 开始时机器人在其中的M位置上(M 一定是 1~N 中的一个) 如果机器人来到1位置,那么下一步只能往右来到2位置; 如果机器人来到N位置,那么下一步只能往左来到 N-1 位置; 如果机器人来到中间位置,那么下一步可以往左走或者往右走; 规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种 给定四个参数 N、M、K、P,返回方法数。
在这里插入图片描述
方法一:

/**** @param N 一共有多少个位置* @param start 开始位置* @param aim 目标位置* @param K 移动步数* @return*/public static int ways1(int N, int start, int aim, int K) {if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) {return -1;}return process1(start, K, aim, N);}// 机器人当前来到的位置是cur,// 机器人还有rest步需要去走,// 最终的目标是aim,// 有哪些位置?1~N// 返回:机器人从cur出发,走过rest步之后,最终停在aim的方法数,是多少?public static int process1(int cur, int rest, int aim, int N) {if (rest == 0) { // 如果已经不需要走了,走完了!//如果当前的位置等于aim位置则方案数加一,否则方案数不变return cur == aim ? 1 : 0;}//rest > 0,还有步数要走// (cur, rest)if (cur == 1) { // 1 -> 2return process1(2, rest - 1, aim, N);}// (cur, rest)if (cur == N) { // N-1 <- Nreturn process1(N - 1, rest - 1, aim, N);}//机器人停在中间位置上// (cur, rest)return process1(cur - 1, rest - 1, aim, N) + process1(cur + 1, rest - 1, aim, N);}

优化Code(空间换时间)

假设我们机器人从七位置触发可以走十步,然后到13位置结束,可以画出如下模型图
在这里插入图片描述
可以看到从7位置出发还有八步要走是个重复值。从而推导cur和rest这两个值是决定最终返回值的key,所以可以采用缓存法

public static int ways2(int N, int start, int aim, int K) {if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) {return -1;}// N+1 * K+1 规模int[][] dp = new int[N + 1][K + 1];//初始化dp缓存表for (int i = 0; i <= N; i++) {for (int j = 0; j <= K; j++) {dp[i][j] = -1;}}// dp就是缓存表// dp[cur][rest] == -1 -> process1(cur, rest)之前没算过// dp[cur][rest] != -1 -> process1(cur, rest)之前算过,返回值,dp[cur][rest]// N+1 * K+1return process2(start, K, aim, N, dp);}/**** @param cur 范围: 1 ~ N* @param rest 范围:0 ~ K* @param aim* @param N* @param dp* @return*/public static int process2(int cur, int rest, int aim, int N, int[][] dp) {//先查缓存表,如果不等于-1表示我之前算过这种情况if (dp[cur][rest] != -1) {return dp[cur][rest];}// 之前没算过,则在此处进行计算int ans = 0;if (rest == 0) {ans = cur == aim ? 1 : 0;} else if (cur == 1) {ans = process2(2, rest - 1, aim, N, dp);} else if (cur == N) {ans = process2(N - 1, rest - 1, aim, N, dp);} else {ans = process2(cur - 1, rest - 1, aim, N, dp) + process2(cur + 1, rest - 1, aim, N, dp);}//返回之前记录此时答案dp[cur][rest] = ans;return ans;}
优化二

如下图:
机器人在2位置,他要到4位置只能走六步,N=5;
在这里插入图片描述
先填入basecase
if(rest == 0) cur ==aim ? 1 : 0;
在这里插入图片描述
然后由如上的代码我们知道最终要的位置是start 和k,主函数最终调的是start和k这个返回值。
在这里插入图片描述
再分析暴力递归代码,找到依赖关系,cur来到1,则依赖值来到cur在2但是rest-1的位置:
在这里插入图片描述
在这里插入图片描述
依赖关系如上图。

再来看cur==N,依赖值来到cur=N-1的位置,rest=rest-1的位置
在这里插入图片描述
在这里插入图片描述
如果是其他位置,则依赖关系如下:
在这里插入图片描述
依赖关系如下图:
在这里插入图片描述
如上三种情况的依赖关系都有了那么则根据依赖关系来填入这张表:
在这里插入图片描述
最终得到2,6的位置为13

 public static int ways3(int N, int start, int aim, int k) {if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1) {return -1;}int[][] dp = new int[N + 1][k + 1];dp[aim][0] = 1;for (int rest = 1; rest <= k; rest++) { //列//第一行dp[1][rest] = dp[2][rest - 1];for (int cur = 2; cur < N; cur++) {dp[cur][rest] = dp[cur-1][rest-1] + dp[cur+1][rest-1];}//最后一行dp[N][rest] = dp[N - 1][rest - 1];}return dp[start][k];}

题目二

给定一个整型数组arr,代表数值不同的纸牌排成一条线玩家A和玩家B依次拿走每张纸牌规定玩家A先拿,玩家B后拿但是每个玩家每次只能拿走最左或最右的纸牌玩家A和玩家B都绝顶聪明请返回最后获胜者的分数【按照最优的方式做决策】。
例:
有如下数组
【50,100,20,10】
先手:10,100
后手:50,20
最后返回110获胜者分数。

思路:
先手:
函数给定f(arr,L,R)这个范围上面拿牌,最后我能获得的最好分数
是多少返回
basecase:
if(L==R) {
只剩一张牌了 return arr[L]
}
第一种选择arr[L] + g(arr,L+1,R)【以后手姿态拿走的牌】
第二种选择arr[R] + g(arr,L,R-1)【以后手姿态拿走的牌】
最后求一个max出来。

后手:
if(L==R) {
return 0;
}
第一种选择,如果对手拿走arr【L】那么后手就在f(arr,L+1,R)先手
第二种选择,如果对手拿走了arr【R】那么后手就在f(arr,L,R-1)先手

	//根据规则返回获胜者的分数public static int win1(int[] arr) {if(arr == null || arr.length == 0) {return 0;}int first = f(arr,0,arr.length-1);int second = g(arr,0,arr.length-1);return Math.max(first,second);}//arr[L..R],先手获得的最好分数返回public static int f(int[] arr,int L,int R) {if(L == R) {return arr[L];}int p1 = arr[L] + g(arr,L+1,R);int p2 = arr[R] + g(arr,L,R-1);return Math.max(p1,p2);}//arr[L...R],后手获得的最好分数返回public static int g(int[]arr,int L,int R) {if(L==R) {return 0;}int p1= f(arr,L+1,R); //对手拿走了L位置的数的最优int p2 = f(arr,L,R-1);//对手拿走了R位置的数的最优//剩下的小的就是先手的//你获得的少,他必然获得的多,你是后手,是由对手做的决定,所以只能拿最小return Math.min(p1,p2);}

优化一(缓存法)

分析如下图可以看出有重复解:
在这里插入图片描述

public static int win2(int[] arr) {if(arr == null || arr.length == 0) {return 0;}int N = arr.length;int[][] fmap = new int[N][N];int[][] gmap = new int[N][N];for(int i = 0 ;ifor(int j =0;jfmap[i][j] = -1;gmap[i][j] = -1;}}int first = f2(arr,0,arr.length-1,fmap,gmap);int second = g2(arr,0,arr.length-1,fmap,gmap);return Math.max(first,second);}private static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {if(gmap[L][R]!=-1) {return gmap[L][R];}// 省掉一个分支,L==Rint ans = 0;if(L!=R) {int p1 = f2(arr,L+1,R,fmap,gmap);int p2 = f2(arr,L,R-1,fmap,gmap);ans = Math.min(p1,p2);}gmap[L][R] = ans;return ans;}private static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {if(fmap[L][R]!= -1) {return fmap[L][R];}int ans = 0;if(L==R) {ans = arr[L];} else {int p1 = arr[L] + g2(arr,L+1,R,fmap,gmap);int p2 = arr[R] + g2(arr,L,R-1,fmap,gmap);ans = Math.max(p1,p2);}fmap[L][R] = ans;return ans;}

优化三(严格表优化)

如下数组
【7,4,16,15,1】
两张表应该如何填:
在这里插入图片描述
在这里插入图片描述
如上分别是f表和g表,首先看basecase,得到如下信息
在这里插入图片描述
在这里插入图片描述
主函数要f这张表里面0-n-1这个格子的值,g要0-n-1的位置,L>R的情况不存在

在这里插入图片描述
在这里插入图片描述
再找f表的依赖,它依赖g表的L+1和R位置,以及L和R-1位置,再找g表的依赖,g依赖于f这张表的L+1和R位置,以及L和R-1位置。
在这里插入图片描述
则可以根据f表的对角线去推出g的对角线,通过g的对角线推f的对角线。

 public static int win3(int[] arr) {if (arr == null || arr.length == 0) {return 0;}int N = arr.length;int[][] fmap = new int[N][N];int[][] gmap = new int[N][N];for (int i = 0; i < N; i++) {fmap[i][i] = arr[i];}for (int startCol = 1; startCol < N; startCol++) {int row = 0;int col = startCol;while (col < N) {fmap[row][col] = Math.max(arr[row] + gmap[row + 1][col],arr[col] + gmap[row][col - 1]);gmap[row][col] = Math.min(fmap[row + 1][col],fmap[row][col - 1]);row++;col++;}}return Math.max(fmap[0][N-1],gmap[0][N-1]);}

相关内容

热门资讯

安卓系统自带的网页,功能与特色... 你有没有发现,每次打开安卓手机,那熟悉的系统界面里总有一个默默无闻的小家伙——安卓系统自带的网页浏览...
美咖云系统安卓版,开启智能生活... 你有没有发现,最近手机上多了一个叫“美咖云系统安卓版”的小家伙?它就像一个魔法师,轻轻一点,就能让你...
安卓系统推荐最好的手机,盘点性... 你有没有想过,拥有一部性能卓越的手机,就像是拥有了移动的宝藏库?在这个信息爆炸的时代,一部好手机不仅...
安卓11系统能精简吗,释放潜能 你有没有发现,随着手机越来越智能,系统也越来越庞大?安卓11系统,这个最新的操作系统,是不是也让你觉...
安卓自动重启系统软件,揭秘原因... 手机突然自动重启,是不是感觉整个人都不好了?别急,今天就来和你聊聊这个让人头疼的安卓自动重启系统软件...
苹果手机x刷安卓系统,探索安卓... 你有没有想过,你的苹果手机X竟然也能刷上安卓系统?是的,你没听错,就是那个一直以来都和我们苹果手机X...
安卓系统智商低吗,智商低下的真... 你有没有想过,为什么安卓系统的智商总被调侃得好像有点低呢?是不是觉得它总是慢吞吞的,有时候还犯点小错...
安卓系统手机联系人,揭秘你的社... 你有没有发现,手机里的联系人列表就像是一个小小的社交圈呢?里面藏着我们的亲朋好友、工作伙伴,甚至还有...
安卓系统免费铃声下载,打造个性... 手机里那首老掉牙的铃声是不是让你觉得有点out了呢?别急,今天就来给你支个招,让你轻松给安卓手机换上...
安卓系统用哪个桌面好,打造个性... 你有没有发现,手机桌面可是我们每天都要面对的“脸面”呢?换一个好看的桌面,心情都能跟着好起来。那么,...
虚拟大师是安卓10系统,功能与... 你知道吗?最近在手机圈里,有个新玩意儿引起了不小的轰动,那就是虚拟大师!而且,更让人惊喜的是,这个虚...
安卓系统与苹果优缺点,系统优缺... 说到手机操作系统,安卓和苹果绝对是两大巨头,它们各有各的特色,就像两道不同的美味佳肴,让人难以抉择。...
安卓win双系统主板,融合与创... 你有没有想过,一台电脑如果既能流畅运行安卓系统,又能轻松驾驭Windows系统,那该有多爽啊?没错,...
安卓系统可精简软件,轻松提升手... 你有没有发现,手机里的安卓系统越来越庞大,软件也越装越多,有时候感觉手机就像个“大肚子”,不仅运行速...
安卓系统基于linux的代码,... 你有没有想过,那个陪伴你每天刷抖音、玩游戏、办公的安卓系统,其实背后有着一套复杂的基于Linux的代...
苹果和安卓的拍照系统,谁更胜一... 你有没有发现,现在手机拍照已经成为我们生活中不可或缺的一部分呢?无论是记录生活的点滴,还是捕捉美丽的...
苹果和安卓系统不同吗,系统差异... 你有没有想过,为什么你的手机里装的是苹果的iOS系统,而朋友的手机却是安卓系统呢?这两种系统,看似都...
安卓系统有多少级,揭秘其多级架... 你有没有想过,那个陪伴我们日常生活的安卓系统,它其实有着丰富的层级结构呢?没错,就是那个让我们的手机...
华为鸿蒙系统与安卓的,技术融合... 你知道吗?最近科技圈可是炸开了锅,华为鸿蒙系统与安卓的较量成为了大家热议的话题。这不,今天我就来给你...
什么安卓手机是苹果系统,搭载苹... 你有没有想过,为什么有些人宁愿花大价钱买苹果手机,而有些人却对安卓手机情有独钟呢?其实,这个问题背后...