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

news2024/10/3 19:12:56

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

      • 题目一
        • 优化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 -> 2
			return process1(2, rest - 1, aim, N);
		}
		// (cur, rest)
		if (cur == N) { // N-1 <- N
			return 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+1
		return 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 ;i<N;i++) {
			for(int j =0;j<N;j++) {
				fmap[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==R
		int 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]);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/136974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Softing与Portainer合作,为工业物联网提供灵活高效的机器连接管理解决方案

Softing工业自动化成为了Portainer的增值经销商。Portainer.io是一个用于管理容器软件应用的平台&#xff0c;而Softing连接产品与其相结合可以使用户更轻松地管理工业物联网解决方案。 &#xff08;左边&#xff1a;Thomas Hilz&#xff0c;Softing工业自动化常务董事&#xf…

创建odoo15项目

Odoo 提供了一种机制来帮助建立一个新模块 python 启动项目名称 scaffold 模块名称 模块创建的文件夹如下两种方式&#xff1a; 1&#xff09;可新建manage.py文件来启动脚手架 import os import sysLIB_PATH os.path.join(os.path.split(os.path.realpath(__file__))[0], .…

【谷粒商城基础篇】基础篇总结

谷粒商城笔记合集 分布式基础篇分布式高级篇高可用集群篇简介&环境搭建项目简介与分布式概念&#xff08;第一、二章&#xff09;基础环境搭建&#xff08;第三章&#xff09;整合SpringCloud整合SpringCloud、SpringCloud alibaba&#xff08;第四、五章&#xff09;前端知…

【kafka-ui】支持kafka with raft的可视化集群管理工具

前言 在早期使用kafka的时候一般使用Kafka Tool或者kafka eagle&#xff0c;前者为桌面软件&#xff0c;后者为浏览器软件。总体来说体验一般&#xff0c;但是还比较够用。 但是从kafka3.3.1开始&#xff0c;已经正式抛弃zookeeper使用自己的仲裁器了&#xff0c;但是上述两种…

什么是网站备案?如何查询网站是否备案?

网站备案是指根据国家法律法规需要网站的开办者向国家有关部门申请的备案.具体分为ICP备案和公安备案。 ICP备案 《互联网信息服务管理办法》指出&#xff0c;互联网信息服务分为经营性和非经营性两类。 国家对经营性互联网信息服务实行许可制度&#xff1b;对非经营…

实验室小分子PEG衍生物之Azido-PEG11-Azide/amine/NHS/COOH 叠氮-十一聚乙二醇-叠氮 1392284-57-9

Azido-PEG11-Azide叠氮-十一聚乙二醇-叠氮 中文名称&#xff1a;叠氮-聚乙二醇(十一甘醇)-叠氮&#xff1b;叠氮-十一聚乙二醇-叠氮 英文名称&#xff1a;Azido-PEG11-Azide 分子式&#xff1a;C24H48N6O11 分子量&#xff1a;596.67 CAS&#xff1a;1392284-57-9 外观&#x…

elasticsearch基础(一)

一、初识elasticsearch 1. 了解ES 1.1 什么是elasticsearch elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。 elasticsearch结合kibana、Logstash、Beats&#xff0c;也就是elastic stack&#xff08;ELK&#xff09;。…

apply,call,bind的作用与区别

1.作用 都可以用来改变this,并立即执行函数 首先来看一个构造函数 function User(name) {this.name name;}let lisi new User(李四)console.log(lisi)// {"name": "李四"}使用call和apply改变this 第一个参数传递改变this指针的这个对象 function Us…

云存储生态构建的技术基因和最佳实践

当云计算、大数据、物联网、人工智能等新技术纷至沓来&#xff0c;我们都在时代的洪流中亲历这样的变化——数字科技深入渗透工作生活&#xff0c;从消费娱乐到生活服务&#xff0c;再到产业革新。 智能化时代&#xff0c;企业 IT 趋势的四大转变 不难发现&#xff0c;以上云…

涉及top名校对IB课程的分数要求

在北美、欧洲&#xff0c;有许多著名的大学乐于接收IB学生&#xff0c;有一些大学还为优秀IB毕业生提供奖励学分、越级的鼓励入学政策。全球有分布在近百个国家的一千余所大学与国际文凭组织(IBO)有稳定的协约关系&#xff0c;确保这些大学承认IB文凭。 什么是IB课程?IB课程项…

FFmpeg 结构体以及核心方法介绍

1.FFmpeg整体结构 ffplay、ffprobe、ffmpeg是上层的三个应用程序 libavutil&#xff1a;核心工具库&#xff0c;其他模块一般都会依赖这个模块做一些基本的音视频处理。 libavformat&#xff1a;文件格式协议库&#xff0c;封装了protocol层和demuxer、Muxer层&#xff0c;使…

ATTCK 01

官网环境链接 漏洞信息 下载好后自行解压 分别在VM中依次打开 配置攻击机和三个客户机的网络环境 其中攻击机选择 kali 客户机为 win7 win8 win2k3 kali的设置 &#xff08;VMnet1&#xff09; win7 的设置 (VMnet1用于连接外网 VMnet2用于连接内网&#xff09; win8 …

滴滴前端一面高频vue面试题及答案

keep-alive 使用场景和原理 keep-alive 是 Vue 内置的一个组件&#xff0c; 可以实现组件缓存 &#xff0c;当组件切换时不会对当前组件进行卸载。 一般结合路由和动态组件一起使用 &#xff0c;用于缓存组件提供 include 和 exclude 属性&#xff0c; 允许组件有条件的进行缓…

数据交换格式

1、什么是数据交换格式 数据交换格式&#xff0c;就是服务器端与客户端之间进行数据传输与交换的格式。 前端领域&#xff0c;经常提及的两种数据交换格式分别是 XML 和 JSON。其中 XML 用的非常少&#xff0c;所以&#xff0c;我们重点要学习的数据交换格式就是 JSON。 2、XML…

数学建模学习笔记-算法(整数规划模型的基本概念)

目录 基本概念 定义 分类&#xff1a; 数学模型 分为 整数规划和松弛的线性规划的关系​编辑 基本概念 定义 数学规划中变量限制为整数时&#xff0c;称为整数规划 线性规划中变量限制为整数时&#xff0c;称为整数线性规划 分类&#xff1a; 变量全限制为整数时&#…

Adversarial Robustness vs. Model Compression, or Both?

众所周知&#xff0c;深度神经网络&#xff08;DNN&#xff09;容易受到对抗性攻击&#xff0c;这种攻击是通过在良性示例中添加精心设计的扰动来实现的。基于最小-最大鲁棒优化的对抗性训练可以提供对抗性攻击的安全概念。然而&#xff0c;对抗性鲁棒性需要比仅具有良性示例的…

二叉树的Morris遍历

Morris 遍历的是指就是避免用栈结构&#xff0c;而是让下层到上层有指针&#xff0c;具体时通过让底层节点指向 null 的空闲指针指回上层的某个节点&#xff0c;从而完成下层到上层的移动。 Morris 遍历的过程&#xff1a; 假设当前节点为cur&#xff0c;初始时cur就是整棵树的…

33-剑指 Offer 34. 二叉树中和为某一值的路径

题目给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有从根节点到叶子节点路径总和等于给定目标和的路径。叶子节点是指没有子节点的节点。示例 1&#xff1a;输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22输出&#…

邮箱中的Qt线程设计

邮箱&#xff08;deepin-mail&#xff09;主要使用Qt框架开发&#xff0c;是一个有大量并行任务且需要监控进度和结果的项目&#xff0c;任务的优先级调整和支持取消回滚也是必不可少。Qt已经为我们提供了多种线程设计的方法&#xff0c;可以满足绝大部分使用场景&#xff0c;但…

钧瓷产业基础架构(SCA架构)是钧瓷产业发展的核心引擎

《钧瓷内参》独立客观&#xff0c;原创前瞻&#xff0c;深度评论&#xff0c;定期推送 钧 瓷 内 参 第3期&#xff08;总第335期&#xff09; 2023年1月3日 钧瓷产业数字化发展的下一个阶段——钧瓷共同体&#xff0c;是钧瓷产业数字化发展的必然趋势。 实现钧瓷共同体的路线…