LeetCode 第 194 场周赛

  • 数组异或操作
    • 思路和代码
  • 保证文件名唯一
    • 思路及代码
  • 避免洪水泛滥
    • 思路及代码
  • 找到最小生成树里的关键边和伪关键边
    • 思路及代码

这次周赛比以往难很多.

数组异或操作

给你两个整数,n 和 start 。

数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 n == nums.length 。

请返回 nums 中所有元素按位异或(XOR)后得到的结果。

思路和代码

直接暴力

class Solution:def xorOperation(self, n: int, start: int) -> int:nums = [start + 2 * i for i in range(n)];ret = 0;for val in nums:ret ^= valreturn ret

保证文件名唯一

给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹:在第 i 分钟,新建名为 names[i] 的文件夹。

由于两个文件 不能 共享相同的文件名,因此如果新建文件夹使用的文件名已经被占用,系统会以 (k) 的形式为新文件夹的文件名添加后缀,其中 k 是能保证文件名唯一的 最小正整数 。

返回长度为 n 的字符串数组,其中 ans[i] 是创建第 i 个文件夹时系统分配给该文件夹的实际名称。

示例:

输入:names = ["kaido","kaido(1)","kaido","kaido(1)"]
输出:["kaido","kaido(1)","kaido(2)","kaido(1)(1)"]
解释:注意,如果含后缀文件名被占用,那么系统也会按规则在名称后添加新的后缀 (k) 。

思路及代码

用一个字典保存文件下一次生成的索引, 举个例子

['a', 'a', 'a', 'a']

那么生成的字典为

d = {'a': 4, 'a(1)':1, 'a(2)': 1, 'a(3)': 1}

例如, 如果后续加入文件a, 先从字典里面查, d['a'] = 4, 那么我们尝试使用a(4), 发现成立, 这时候字典更新为

d = {'a': 5, 'a(1)':1, 'a(2)': 1, 'a(3)': 1, 'a(4)': 1}

如果下一次加入文件a(1) 那么我们知道a(1)下一次对应生成的索引为1,那么尝试生成a(1)(1), 也是成立的.

考虑另外一种情况, 例如现在字典为

d = {'a':5, 'a(1)':1, 'a(1)(1)':1}

下一次加入a(1), 首先下一次生成的索引应该为a(1)(1), 此时发现被占用, 所以对索引增加操作a(1)(2) 此时成立, 此时字典需要更新a(1)a(1)(2), 更新为, 通过一个循环可以处理

d = {'a':5, 'a(1)': 1 + 2, 'a(1)(1)': 1, 'a(1)(2)': 1}

代码如下: python

class Solution:def getFolderNames(self, names: List[str]) -> List[str]:visited = collections.defaultdict(int)ret = []for name in names:            if name not in visited:ret.append(name)visited[name] += 1else:n = visited[name]cnt = 0while name + '(' + str(n + cnt)+ ')' in visited:cnt += 1;visited[name] += cnt + 1;name += '(' + str(n + cnt)+ ')'ret.append(name)visited[name] += 1return ret

CPP:

class Solution {public:vector<string> getFolderNames(vector<string>& names) {unordered_map<string, int> counter;vector<string> ret;for(auto name: names){if (counter.count(name)){int &n = counter[name];int cnt = 0;while (counter.count(name + "(" + to_string(n + cnt) + ")")) ++cnt;ret.push_back(name + "(" + to_string(n + cnt) + ")");++counter[name + "(" + to_string(n + cnt) + ")"];n += cnt + 1;}else{ret.push_back(name);++counter[name];}}return ret;}
};

避免洪水泛滥

你的国家有无数个湖泊,所有湖泊一开始都是空的。当第 n 个湖泊下雨的时候,如果第 n 个湖泊是空的,那么它就会装满水,否则这个湖泊会发生洪水。你的目标是避免任意一个湖泊发生洪水。

给你一个整数数组 rains ,其中:

  • rains[i] > 0 表示第 i 天时,第 rains[i] 个湖泊会下雨。

  • rains[i] == 0 表示第 i 天没有湖泊会下雨,你可以选择 一个 湖泊并 抽干 这个湖泊的水。
    请返回一个数组 ans ,满足:

  • ans.length == rains.length

  • 如果 rains[i] > 0 ,那么ans[i] == -1

  • 如果 rains[i] == 0ans[i] 是你第 i 天选择抽干的湖泊。

如果有多种可行解,请返回它们中的任意一个 。如果没办法阻止洪水,请返回一个 空的数组 。

请注意,如果你选择抽干一个装满水的湖泊,它会变成一个空的湖泊。但如果你选择抽干一个空的湖泊,那么将无事发生(详情请看示例 4)。
示例:

输入:rains = [69,0,0,0,69]
输出:[-1,69,1,1,-1]
解释:任何形如 [-1,69,x,y,-1], [-1,x,69,y,-1] 或者 [-1,x,y,69,-1] 都是可行的解,其中 1 <= x,y <= 10^9

思路及代码

一个最朴素的思路是回溯, 即当面某天下雨时, 随机抽一个满谁的湖泊, 看最后能否成立 ,超时.

如果同一个湖泊在第i天和第j被填满, 我们应该在ij 之间选择不下雨的一天, 来抽干该湖泊的水, 选择的策略是如果其中很多天不下雨, 那么选择在第i天之后且最接近第i天的不下雨的那天

那么思路可以是, 遍历rains, 维护一个不下雨天的索引, 显然这个索引是递增的, 同时记录被填满的湖泊和对应天数, 当遍历到某一天出现下雨到同一个湖泊这种情况, 则找出上一次这个湖泊被填满那天i 用二分搜索O(log⁡n)O(\log n)O(logn), 搜索iupper_bound(大于 i 的最小索引), 将次索引的结果填入对应胡泊序号, 同时在维护的索引数据删除这个位置的元素O(n)O(n)O(n)(对于 python). (Cpp可以用 set 保证查找和删除都是log⁡n\log nlogn)

代码如下:

class Solution:def avoidFlood(self, rains: List[int]) -> List[int]:import bisectids = []v = {}ret = [0] * len(rains)for idx, val in enumerate(rains):if val:ret[idx] = -1if val in v:i = bisect.bisect_right(ids, v[val])if i == len(ids):return []else:ret[ids[i]] = valids.pop(i)v[val] = idxelse:ids.append(idx)for idx, val in enumerate(ret):if val == 0:ret[idx] = 1return ret

进一步优化: 使用优先队列, 首先逆序遍历rains, 使用数组记录当前下雨湖泊下一次下雨的天数.

顺序遍历rains, 使用优先队列记录[下一个下雨天数, 对应胡泊], 当某天不下雨的时候, 开始出队. Cpp 代码如下:

class Solution {public:vector<int> avoidFlood(vector<int>& rains) {unordered_map<int, int> pos;int n = rains.size();vector<int> nxt(n, n);for(int i = n - 1; i >=0; --i){if(rains[i] == 0 || pos.count(rains[i]) == 0){pos[rains[i]] = i;}else{nxt[i] = pos[rains[i]];pos[rains[i]] = i;} }vector<int> ret;priority_queue<pair<int, int> > que;unordered_set<int> visited;for (int i = 0; i < n; ++i){if (rains[i] == 0){if(que.empty()) ret.push_back(1);else{auto p = que.top();que.pop();ret.push_back(p.second);visited.erase(p.second);}}else{if (visited.count(rains[i])) return {};if(nxt[i] != n){que.push(make_pair(-nxt[i], rains[i]));visited.insert(rains[i]);}ret.push_back(-1);}}return ret;}
};

找到最小生成树里的关键边和伪关键边

给你一个 n 个点的带权无向连通图,节点编号为 0n-1 ,同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti] 表示在 fromitoi 节点之间有一条带权无向边。最小生成树 (MST) 是给定图中边的一个子集,它连接了所有节点且没有环,而且这些边的权值和最小。

请你找到给定图中最小生成树的所有关键边和伪关键边。如果从图中删去某条边,会导致最小生成树的权值和增加,那么我们就说它是一条关键边。伪关键边则是可能会出现在某些最小生成树中但不会出现在所有最小生成树中的边。

请注意,你可以分别以任意顺序返回关键边的下标和伪关键边的下标。

思路及代码

参考零神题解, 由于数据量不大, 反复使用 Kruskal 算法, 关于 MST 和 Kruskal 算法, 参考我的这篇博客

其中, 注意关键边和伪关键边的含义:
关键边: 所有 MST 共享的边
伪关键边: 任意 MST 的有一条边, 且这条边不是关键边.

流程如下:

  1. 按照权值对边进行排序
  2. 调用 Kruskal 计算 MST 的值, 即为v
  3. 任意去掉一条边, 调用 Kruskal 计算 MST 的 value, 如果v != value, 说明这条边为关键边, 记录下所有关键边
  4. 将关键边先连接起来, 选定一条剩余边, 将其强制加入 MST 中, 然后使用 Kruskal 生成 MST , 如果value == v 则这条剩余边为伪关键边.

代码如下

class DSU{public:vector<int> parent;DSU(DSU &dsu){int n = dsu.parent.size();parent.resize(n);for(int i = 0; i < n; ++i) parent[i] = dsu.parent[i];}DSU(int n){parent.resize(n);for(int i = 0; i < n; ++i) parent[i] = i;}int find(int x){if (parent[x] != x)parent[x] = find(parent[x]);return parent[x];}void union_(int x, int y){int rx = find(x), ry = find(y);if (rx != ry){parent[rx] = ry;}}bool isCircle(int x, int y){return find(x) == find(y);}void clear(){for(int i = 0; i < parent.size(); ++i) parent[i] = i;}
};bool cmp(vector<int> &a, vector<int> &b){return a[2] < b[2];}class Solution {public:vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>& edges) {int v = 0;for(int i = 0; i < edges.size(); ++i){edges[i].push_back(i);}DSU dsu(n);sort(edges.begin(), edges.end(), cmp);int cnt = 0;for(auto &edge: edges){if(!dsu.isCircle(edge[0], edge[1])){++cnt;v += edge[2];dsu.union_(edge[0], edge[1]);}if(cnt == n - 1) break;}vector<int > ret1;unordered_set<int> st;for (int cur = 0; cur < edges.size(); ++ cur){dsu.clear();int value = 0;int ccnt = 0;for(int i = 0; i < edges.size(); ++i){auto &edge = edges[i];if(i == cur) continue;if(!dsu.isCircle(edge[0], edge[1])){++ccnt;value += edge[2];dsu.union_(edge[0], edge[1]);}if(ccnt == n - 1) break;}if (value != v) {ret1.push_back(edges[cur][3]); st.insert(cur);}}vector<int> ret2;int rest = n - 1 - st.size();dsu.clear();int st_v = 0;for(auto &i: st) {dsu.union_(edges[i][0], edges[i][1]);st_v += edges[i][2];}for(int cur = 0; cur < edges.size(); ++cur){if(st.count(cur)) continue;DSU tmp(dsu);int value = st_v;int _rest = rest;if(tmp.isCircle(edges[cur][0], edges[cur][1])) continue;else{value += edges[cur][2];--_rest;tmp.union_(edges[cur][0], edges[cur][1]);}for(int i = 0; i < edges.size(); ++i){if(i == cur || st.count(i)) continue;if(!tmp.isCircle(edges[i][0], edges[i][1])){--_rest;value += edges[i][2];tmp.union_(edges[i][0], edges[i][1]);}if(_rest == 0) break;}if (_rest == 0 && value == v) ret2.push_back(edges[cur][3]);}return {ret1, ret2};}
};

LeetCode 第 194 场周赛相关推荐

  1. [算法]LeetCode第194场周赛202006021

    第194场周赛 20200621 1486. 数组异或操作 题目描述1 给你两个整数,n 和 start . 数组 nums 定义为:nums[i] = start + 2*i(下标从 0 开始)且 ...

  2. LeetCode第 227 场周赛题解

    LeetCode第 227 场周赛题解 检查数组是否经排序和轮转得到 原题链接 https://leetcode-cn.com/problems/check-if-array-is-sorted-an ...

  3. LeetCode第187场周赛(Weekly Contest 187)解题报告

    差点又要掉分了,还好最后几分钟的时候,绝杀 AK.干巴爹!!! 第一题:思路 + 模拟暴力. 第二题:线性扫描. 第三题:双指针(滑动窗口) + 优先队列. 第四题:暴力每一行最小 k 个 + 优先队 ...

  4. Acwing第72场周赛+Leetcode第314场周赛

    Acwing第72场周赛 第一题:AcWing 4624. 最小值 分析:向下取整可以用到math.h头文件中的floor()函数,最后输出时套用两个min()函数求三个数的最小值即可. 代码: #i ...

  5. Leetcode第 310 场周赛 补打

    Leetcode 第310场周赛 自己赛后打了一下,记录了一下时间,大概15min A 3题,第四题是写不出来,然后学习了一天线段树(真的强). 思路: 1.排序后统计偶数的数目 2.遍历扫一遍,用直 ...

  6. LeetCode第176场周赛(Weekly Contest 176)解题报告

    又是一周掉分之旅,我发现,LeetCode周赛的数据好水,所以有的时候,实在没思路,先暴力解决试试(即使分析出时间复杂度会超时),比如第二题和第三题都可以暴力通过,GG思密达. 这周主要使用了数据结构 ...

  7. Leetcode第321场周赛补题

    Leetcode第321场周赛补题 第一题:6245. 找出中枢整数 - 力扣(LeetCode) 分析:由于数组中是差值为1的等差数列,所以可以直接用等差数列求和公式的朴素法更加简便的解决这题,,其 ...

  8. LeetCode第 310 场周赛

    文章目录 前言 第86场双周赛情况 题目复盘+题解 题1:6176. 出现最频繁的偶数元素[easy] 题2:6176. 出现最频繁的偶数元素[medium] 题3:6178. 将区间分为最少组数[m ...

  9. leetCode第199场周赛学习

    199场周赛 1528. 重新排列字符串 给你一个字符串 s 和一个 长度相同 的整数数组 indices . 请你重新排列字符串 s ,其中第 i 个字符需要移动到 indices[i] 指示的位置 ...

最新文章

  1. 序列两两比对算法_【陪你学生信】八、序列两两比对
  2. TortoiseGit密钥的配置
  3. 如何精确评估开发时间的 4 个小套路?
  4. # 异运算_小学六年级数学知识点总结-03分数的混合运算
  5. tensorflow,显卡驱动,CUDA傻傻分不清
  6. Jekyll报'Tag was never closed'错误
  7. 如何在面试中介绍自己的项目经验,90%的人都做错了!
  8. boost::property_tree模块自定义 ptree 的 data_type 需要执行的操作
  9. 网络驱动器设备:iscsi服务器
  10. 解决方案 | tensorflow安装慢解决方案
  11. 《UNIX网络编程 卷1:套接字联网API(第3版)》——第2章 传输层:TCP、UDP和SCTP 2.1概述...
  12. 外设驱动库开发笔记14:DS18B20温度变送器驱动
  13. 利用优先级队列实现堆栈
  14. 如何解决回归任务数据不均衡的问题?
  15. 19年计算机专硕学硕报名人数,2019年研招报考数据出炉!专硕的报考人数赶超学硕!...
  16. Python 3.x中reduce()函数完整用法
  17. 200 行代码解读国产数据库阿里 OceanBase 的速度源头!| CSDN 博文精选
  18. 用python实现bt下载_python实现bt种子 torrent转magnet
  19. DirectX 9 游戏汉化详解
  20. 电磁屏蔽技术的三种主要方法

热门文章

  1. 【2021笔记本选购指南】让你了解笔记本少走弯路(内存、硬盘、屏幕等参数解读)
  2. html如何选择本地图片,Html5本地图片读取及裁剪
  3. 4月2日云栖精选夜读 | 对话行癫:解密阿里云顶层设计和底层逻辑...
  4. 51单片机C语言程序100例分析(1)IO+C语言+头文件
  5. 小米AX1800开SSH权限
  6. package.json文件是个什么东东?
  7. 用户标签体系的设计和效果评估
  8. 设计模式之责任链模式
  9. java piggy,piggymetrics
  10. adb bugreport -- Failed to get bugreportz version