【上分日记】377场周赛(图论 + dp)

文章目录

  • 前言
  • 正文
    • 1.2975. 移除栅栏得到的正方形田地的最大面积
    • 2.2976. 转换字符串的最小成本 I
    • 3.2977. 转换字符串的最小成本 II
  • 总结
  • 后文

前言

 本场周赛,后两题都涉及到了图论的最短路径(克鲁斯卡尔算法)的知识,恰巧又没学过,所以博主本周基本都在补图论的知识,所以这场周赛的题解虽迟但到。

 这场周赛,博主也只写出一题,第二道还超时了(hhh,菜鸡勿喷)。下面博主就来总结一下,没写出来的三道题。

正文

  • 如果有图论知识欠缺的,可看博主总结的这篇博客:图论与并查集。

1.2975. 移除栅栏得到的正方形田地的最大面积

  • 题目链接 :移除栅栏得到的正方形田地的最大面积

  • 注意事项:

 博主在做这道题时,就没有分析好题,长和宽独立可以分别枚举,而博主直接N3 暴力枚举,结果很显然超时了。

 我们先来分析一下,为什么长和宽可以分别进行枚举?

 要移除栅栏得到正方形,可以转换为横着取出两根栅栏,其差作为长,竖着取出两根栅栏作为宽,围成面积,且长与宽相等,求其面积。

  • 因此,横着取两根栅栏和竖着取两根栅栏是互不影响的,因此独立。

  • 题目思路:
  1. 我们可以 暴力枚举 横着取出两根栅栏其形成的差作为长 的所有可能。
  2. 接着 暴力枚举 竖着 取出两根栅栏形成的差作为宽 的所有可能。
  3. 求两个集合相同元素的最大值。

  • 实现代码:
class Solution 
{
public:const int MOD = 1e9 + 7;int maximizeSquareArea(int m, int n, vector<int>& hFences,vector<int>& vFences) {//将水平的栏杆与垂直的栏杆分别求出,并求出并集的最大值。    unordered_set<int> rows,cols;//将所有栏杆都扔进数组,先进行一步预处理。hFences.push_back(1);hFences.push_back(m);vFences.push_back(1);vFences.push_back(n);sort(hFences.begin(),hFences.end());sort(vFences.begin(),vFences.end());int hsz = hFences.size();int vsz = vFences.size();//将边长的所有可能性暴力枚举出来。//长for(int i = 0; i < hsz; i++){for(int j = i + 1; j < hsz; j++){rows.insert(hFences[j] - hFences[i]);}}//宽for(int i = 0; i < vsz; i++){for(int j = i + 1; j < vsz; j++){cols.insert(vFences[j] - vFences[i]);}}// 求出并集的最大值。long long edge = INT_MIN;for(long long val : rows){if(cols.count(val)){edge = max(edge,val);}}  if(edge == INT_MIN){//说明没有。return -1;}return (edge * edge) % MOD;}   
};

2.2976. 转换字符串的最小成本 I

  • 题目链接:转换字符串的最小成本 I

  • 注意细节:
  1. 因为只有26个小写字母,我们只需开26*26的矩阵即可(映射一下)。
  2. orignal [ i ] -> change [ i ], 需花费 cost[ i ] 中可能存在重复的,比如同时存在a->b,花费 2,和 a->b 花费 3 ,此时求最短路径我们需要挑出最小的作为路径。
  3. 对于相同元素之间的路径,无需进行花费,所以路径我们需初始化为0。
  4. 分析图为有向图。
  • 题目思路:
  1. 用二维矩阵(26 * 26)存放 字母1 到 字母 2的最短路径。
  2. 对角线初始化为0,即相同元素之间最小路径的花费为0。
  3. 用弗洛伊德算法,根据 orignal,change,cost[ i ] 求出最短路径。
  4. 根据最短路径的矩阵,用source 和 target 求出转换的最小成本。

  • 实现代码:
class Solution {
public:long long minimumCost(string source, string target, vector<char>& original, vector<char>& changed, vector<int>& cost) {vector<vector<int>> min_dst(26,vector<int>(26,INT_MAX));//对角线初始化为0,因为 a->a的最小成本是0for(int i = 0; i < 26; i++)min_dst[i][i] = 0;//剩下的从original[i]->changed[i]//细节:可能存在相同的,因此要从相同中选出最小的那一个。for(int i = 0; i < original.size(); i++){int o_i = original[i] - 'a';int c_i = changed[i] - 'a';min_dst[o_i][c_i] = min(min_dst[o_i][c_i],cost[i]);}//用弗洛伊德算法,求出多源最短路径for(int k = 0; k < 26; k++){for(int i = 0; i < 26; i++){for(int j = 0; j < 26; j++){if(min_dst[i][k]!= INT_MAX && min_dst[k][j] != INT_MAX){min_dst[i][j] = min(min_dst[i][j], min_dst[i][k] + min_dst[k][j]);}}}}//最后求直接遍历求解即可long long ans = 0;for(int i = 0; i < source.size(); i++){int s_i = source[i] - 'a';int t_i = target[i] - 'a';if(min_dst[s_i][t_i] == INT_MAX)return -1;elseans += min_dst[s_i][t_i];}return ans;}
};

3.2977. 转换字符串的最小成本 II

  • 题目链接:转换字符串的最小成本 II

  • 说明:

 博主看题解都感觉吃力,因为连字典树都还没有用过,还得先去补一补字典树的知识。有需要的C友,可以看下面的视频与习题快速了解字典树。

  • 视频链接: 字典树
  • 习题:208. 实现 Trie (前缀树)

  • 细节: original[i] 与 change[i] 的字符串可能完全不相同,因此我们初始化的矩阵为 original.size() + change.size();

  • 题目思路:
  1. 借助字典树,将字符串依次生成编号,即将字符串转化为点。
  2. 用二维矩阵存放 字符串1 到 字符串2的最短路径。
  3. 对角线初始化为0,即相同字符串之间最小路径的花费为0。
  4. 用弗洛伊德算法,根据 orignal,change,cost[ i ] 求出最短路径。
  5. 使用记忆化搜索 —— dp, 求出以 i 为起点的用source[i]之后的字符串 转换为 target[i]即之后的字符串 的最小成本。

  • 实现代码:
//字典树的结点,本题用于生成字符串的下标。
struct Node
{Node* arr[26] = {nullptr};int _id = -1;
};class Solution {
public:long long minimumCost(string source, string target, vector<string>& original, vector<string>& changed, vector<int>& cost) {//第一步:先将字符串用生成编号(下标表示)/*此处 original 与 changed 数组的字符串可能完全不同,因此最多能生成 original.size() + changed.size() 个字符串的编号。 */int sz = original.size() + changed.size();vector<vector<int>> dst(sz, vector<int>(sz,INT_MAX));/*此处将字典树进行初始化,并给出字符串转换的生成函数。*/int id = 0; //最后的id即为生成字符串的个数。Node* root = new Node;auto GetStrIndex = [&](string& str)->int{Node* cur = root;for(char ch : str){if(!cur->arr[ch - 'a']){cur->arr[ch - 'a'] = new Node;}cur = cur->arr[ch - 'a'];}//此处的cur,即为单词的结尾,我们要给一个编号,且要判重。if(cur->_id < 0){cur->_id = id++;}return cur->_id;};/*用字典树初始化路径矩阵。且需注意,这里的路径可能会重复,因此需要取出最小的。*//* 先对对角线的元素初始化为0*/for(int i = 0; i < sz; i++){dst[i][i] = 0;}for(int i = 0; i < original.size(); i++){int o_i = GetStrIndex(original[i]);int c_i = GetStrIndex(changed[i]);dst[o_i][c_i] = min(dst[o_i][c_i],cost[i]);}// 第二步:用弗洛伊德算法,求出任意两点之间的最短路径for(int k = 0; k < id; k++){for(int i = 0; i < id; i++){/*if(dst[i][k] == INT_MAX)continue;//不加此优化会慢上三倍作用。*/ for(int j = 0; j < id; j++){if(dst[i][k] != INT_MAX && dst[k][j] != INT_MAX)dst[i][j] = min(dst[i][j],dst[i][k] + dst[k][j]);}}}//第三步:使用记忆化搜索(dp),遍历求出以i为起点转换为target的最小成本。//此处需要开辟一个数组,用于保存以i为起点的转换为target的最小成本int ssz =  source.size();vector<long long> start(ssz,-1);/*细节:此处需使用包装器可让lambda表达式用于递归 */function<long long(int)> dfs = [&](int i)->long long{if(i == ssz)return 0;long long &ret = start[i];/*此处ret如果不为-1,则说明已经找到了,无需再进行找,直接返回即可。*/if(ret != -1){return ret; }/*先对ret进行初始化为 LONG_LONG_MAX / 2,当取不到时我们返回此结果即可*/ret = LONG_LONG_MAX / 2;/* 分情况:1.source[i] == target[i],可以不进行修改,此时可能 dfs(i+1)为最小成本2.以i为起点的往后的字符串也有可能为最小成本。因此需要取两种情况的最小值*/if(source[i] == target[i])ret = dfs(i+1);/*遍历之后的字符串, 即[i,j]之间的字符串看是否存在最短的路径*/Node* sstr = root,*tstr = root; for(int j = i; j < ssz; j++){int sstr_j = source[j] - 'a';int tstr_j = target[j] - 'a'; sstr = sstr->arr[sstr_j];tstr = tstr->arr[tstr_j];if(sstr == nullptr || tstr == nullptr){//说明之后也不可能存在字符串,直接break即可break;}else if(sstr->_id < 0 || tstr->_id < 0){//说明还没有取到字符串continue;}else{int sstr_id = sstr->_id;int tstr_id = tstr->_id;/*因为有可能都在source和change数组中,因此我们还需判断一下。*/if(dst[sstr_id][tstr_id] != INT_MAX){ret = min(ret,dst[sstr_id][tstr_id] + dfs(j+1));}}}return ret;};long long answer = dfs(0);return answer < LONG_LONG_MAX / 2 ? answer : -1;}
};
  • 说明:本题解主要参考灵神的题解,并进行了较为详细的注释。
  • 补充语法细节:这里的lambda 之所以 封装成 function 包装器的形式,是为了在让lambda表达式在内部调用自己,即能够递归。

总结

  1. 第一题的代码主要使用了暴力枚举 + unordered_set进行实现,总的时间复杂度为O(N2)。
  2. 第二题的代码主要使用了弗洛伊德算法,求最短路径。
  3. 第三题在第二题的基础上,用字典树对字符串进行编号,通过弗洛伊德算法进而求最短路径,然后用记忆化搜索求出字符串转换的最短路径。
  • 本场周赛主要用到算法知识:弗洛伊德算法 + 字典树 + dp。

  • 本篇文章的分析到这里就结束了,如果感到有所收获,不妨点个赞鼓励一下吧!

后文

我是舜华,期待与你的下一次相遇!

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

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

相关文章

文献阅读:LoRA: Low-Rank Adaptation of Large Language Models

文献阅读&#xff1a;LoRA: Low-Rank Adaptation of Large Language Models 1. 文章简介2. 方法介绍3. 实验 & 结论 1. 基础实验 1. Bert系列模型2. GPT系列模型 2. 消解实验 1. 作用矩阵考察2. 中间维度考察3. 扰动程度分析 4. 总结 & 思考 文献链接&#xff1a;htt…

SLAM学习入门--机器学习

文章目录 机器学习逻辑回归&#xff08;LR&#xff09;基本原理为什么 LR 要使用 sigmoid 函数&#xff1f;LR 可以用核函数么&#xff1f;为什么 LR 用交叉熵损失而不是平方损失&#xff1f;LR 能否解决非线性分类问题&#xff1f;LR为什么要离散特征&#xff1f;逻辑回归是处…

Linux文件系统结构及相关命令2 什么是Shell? help cd cd的用法 ls 的用法

Shell 是一种用于与操作系统进行交互的命令行解释器。它是用户与操作系统内核之间的接口&#xff0c;接受用户的命令并将其传递给操作系统进行执行。 在大多数的 Unix-like 操作系统&#xff08;如 Linux 和 macOS&#xff09;以及类 Unix 系统中&#xff0c;Shell 是默认的命…

10 HXCodec

前言 这个主要是提供了日常的相关 编码解码体系 的工具包, 依赖于fastjson 和 netty-all 里面主要包含了一些 摘要的生成, base64编码解码, ByteBuf操作api, Des编码解码, 文件操作, 十六进制操作, io操作, md5操作, 属性操作, rsa操作, socket操作 对象 序列化为字节序列, …

Qlib从入门到精通

前面谈到了简单的一个示例代码&#xff0c;实际上里面的策略源码和模型回测源码都需要好好了解&#xff0c;他这个回测系统和我之前用到的回测策略代码有不一样的地方&#xff0c;作为一个量化策略攻城狮&#xff0c;掌握源码是基本的技能。 Qlib内置了A股、美股两个市场的历史…

如何开发一个google插件(二)

前言 在上一篇文章如何开发一个google插件(一)里主要介绍了google插件的基本结构。 在这篇文章中主要结合reactwebpack进行一个代码演示&#xff0c;源码地址&#xff1a;源码地址 下载源码后打开浏览器的扩展程序管理->加载已解压的扩展程序&#xff0c;即可调试插件 此…

在线客服选择要点分析:如何挑选适合您需求的客服解决方案

选择一款好的在线客服系统&#xff0c;可以帮助企业多渠道的触达客户&#xff0c;与客户进行高效的沟通&#xff0c;最终达成转化的目的。 市面上现在成熟的客服系统产品有很多&#xff0c;企业在进行选择时要考虑以下几点&#xff1a; 1、企业需求 市场上的客服系统产品主要…

10、复制

复制概述 复制解决的基本问题是让一台服务器的数据与其他服务器保持同步。一台主库的数据可以同步到多台备库上&#xff0c;备库本身也可以被配置成另外一台服务器的主库。主库和备库之间可以有多种不同的组合方式。 Mysql 支持两种复制方式&#xff1a;基于行的复制和基于语句…

向表达式添加括号后的最小结果

说在前面 &#x1f388;不知道大家对于算法的学习是一个怎样的心态呢&#xff1f;为了面试还是因为兴趣&#xff1f;不管是出于什么原因&#xff0c;算法学习需要持续保持。 题目描述 给你一个下标从 0 开始的字符串 expression &#xff0c;格式为 “num1num2” &#xff0c;…

ArkTS - 组件生命周期

一、先说下自定义组件 在arkTs中&#xff0c;自定义组件分为两种&#xff08;我的总结&#xff09;&#xff1a; 一种是&#xff1a;根组件&#xff0c;就是被装饰器Entry装饰的入口组件&#xff0c;这也是自定义组件(父组件)。 另一种是&#xff1a;没有被Entry装饰的自定义…

跟我学c++中级篇——再谈C++20中的协程

一、协程 在前面分析过协程是什么&#xff0c;也对c20中的协程的应用进行了举例说明。所以这次重点分析一下c20中的整体构成及应用的方式。等明白了协程是如何动作的&#xff0c;各种情况下如下何处理相关的事件&#xff0c;那么在以后写协程时就不会死搬硬套了。 二、整体说…

分布式存储考点梳理 + 高频面试题

欢迎来到分布式存储模环节&#xff0c;本文我将和你一起梳理面试中分布式系统的数据库的高频考点&#xff0c;做到温故知新。 面试中如何考察分布式存储 广义的分布式存储根据不同的应用领域&#xff0c;划分为以下的类别&#xff1a; 分布式协同系统 分布式文件系统 分布式…

Xline command 去重机制(一)—— RIFL 介绍

为什么要对 command 去重&#xff1f; 在一个接收外部 command 的系统中&#xff0c;通常一个 command 至少要执行一次&#xff0c;我们称其为 at-least-once semantics。如果一个 command 执行失败&#xff0c;系统内部经常会实现一套重试结构来尝试恢复这个问题&#xff0c;…

HTML 基础

文章目录 01-标签语法标签结构 03-HTML骨架04-标签的关系05-注释06-标题标签07-段落标签08-换行和水平线09-文本格式化标签10-图像标签图像属性 11-路径相对路径绝对路径 12-超链接标签13-音频14-视频 01-标签语法 HTML 超文本标记语言——HyperText Markup Language。 超文本…

【分布式配置中心】聊聊Apollo的安装与具体配置变更的原理

【管理设计篇】聊聊分布式配置中心 之前就写过一篇文章&#xff0c;介绍配置中心&#xff0c;但是也只是简单描述了下配置中心的设计点。本篇从apollo的安装到部署架构到核心原理进一步解读&#xff0c;大概看了下apollo的原理&#xff0c;感觉没有必要深究&#xff0c;所以就…

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项样题卷③

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第3套&#xff09; 目录 2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第3套&#xff09; 模块…

2024黑龙江省职业院校技能大赛暨国赛选拔赛应用软件系统开发赛项(高职组)赛题第1套

2024黑龙江省职业院校技能大赛暨国赛选拔赛 应用软件系统开发赛项&#xff08;高职组&#xff09; 赛题第1套 竞赛说明 目录 需要竞赛源码资料可私信博主&#xff01; 竞赛说明 模块一&#xff1a;系统需求分析 任务1&#xff1a;制造执行MES—质量管理—质检标准&#xff…

FreeRTOS学习--49讲 事件标志位

事件标志位&#xff1a; 用一个bit位来表示事件是否发生&#xff0c;只有0(未发生)和1(已发生)两种状态 事件组&#xff1a;事件组是一组事件标志的集合&#xff0c;一组事件组包含了EventBites_t数据类型的变量&#xff0c;该变量高8未不能作为事件标志&#xff0c;用于存储控…

以一种访问权限不允许的方式做了一个访问套接字的尝试

python -m http.server 启动失败 原因是端口被占用&#xff0c;但是使用 netstat -ano|findstr 8000 却没发现占用&#xff0c;最后发现是hyper-v占用了&#xff0c;要给容器使用。 使用命令 netsh int ipv4 show dynamicport tcp可以查看TCP 动态端口范围的情况。 netsh int…

人机对话--关于意识机器

人机对话——关于意识机器 这段内容是我和《通义千问》的对话。这本身展示的是人工智能的效果&#xff0c;同时这里面的内容也有人工智能相关&#xff0c;与各位分享。 我&#xff1a;阿尼尔赛斯 《意识机器》这本书写的是什么&#xff1f; 通义千问&#xff1a; 阿尼尔赛斯…