A. 找卧底

题目描述

牛牛今天和大家玩了一个新游戏,除了牛牛以外还有 n 个人参加游戏,现在这 n 个人中的每个人从 [1, n] 中选择一个数字,保证选出的数字均不重复。牛牛作为第 n + 1 个人,充当卧底的角色,要求卧底从 1 到 n 中选择一个数字,现在将 n + 1 个数字重新打乱顺序,请找出卧底选择的数字是多少。

备注:

其中1 <= n <= 100000。
要求时间复杂度为 O(n),额外空间复杂度为 O(1)。

示例1

输入

4,[1,2,1,4,3]

输出

1

解法一:原地哈希

思路分析

这种找重复数字的题目还挺常见的。最直接的方法是用哈希表存储数字,遇到新数字判断它是否在哈希表里,若在就说明重复。

不过题目要求空间复杂度为 O(1)O(1)O(1)。那么就不能用内置哈希表了。

注意到数字范围是 [1, n],并且数组长度为 n + 1,因此我们可以把原数组当成哈希表,通过交换,将数字放到对应下标的位置即可。

例如:遇到了数字10,那么我们就去看看下标为 10 的数字是不是 10,是的话说明数字重复了,否则交换这两个数字。

代码实现

public int search (int n, int[] a) {while(a[0] != a[a[0]]){int temp = a[0];a[0] = a[a[0]];a[temp] = temp;}return a[0];
}

解法二:求和相减

思路分析

记卧底选的数字是 b,那么 ∑i∈[0,n]a[i]=∑x∈[1,n]x+b\sum_{i \in [0,n]} a[i] = \sum_{x \in [1,n]} x + b∑i∈[0,n]​a[i]=∑x∈[1,n]​x+b。显然求出两个求和的结果,相减即可得到答案。

代码实现

public int search (int n, int[] a) {int sum = 0;for(int i = 0; i <= n; i++) sum += a[i];for(int i = 1; i <= n; i++) sum -= i;return sum;
}

解法三:位运算

思路分析

剑指 offer 里有一道很经典的题:给一个数组,只有一个数字仅出现一次,其他数字都出现了两次,问如何找到这个数字。

那题是利用了异或运算得到答案的。并且我们知道异或的结果与奇偶次有关,并不局限于一次两次的限定。

这题是只有一个数字出现了两次,其他数字都仅出现一次。如何与那题关联起来呢?

很简单,就让 1 ~ n 再出现一次,这样只有一个数字出现了三次,其他数字都出现了两次。这样就可以用异或来解决啦。

代码实现

public int search (int n, int[] a) {int x = 0;for(int i = 1; i <= n; i++) x ^= i;for(int i = 0; i <= n; i++) x ^= a[i];return x;
}

B. 父子情深

题目描述

在一颗有 n 个结点且以 1 为根节点树上,起初每个结点的初始权值为 0 。

现在有 q 次操作,每次操作选择将以 rir_iri​ 为根节点的子树上的所有结点权值增加 xix_ixi​ 。

求 q 次操作后从 1 到 n 每个结点的权值。

输入

第一个参数为 n,1 ≤ n ≤ 100,000

第二个参数为边 (ui,vi)(u_i, v_i)(ui​,vi​) 的集合,其中 (ui,vi)(u_i, v_i)(ui​,vi​) 表示结点 uiu_iui​ 与结点 viv_ivi​ 之间有一条边,1≤ui,vi≤n1 ≤ u_i, v_i ≤ n1≤ui​,vi​≤n

第三个参数为 q,1 ≤ q ≤ 100,000

第四个参数为 q 次询问的 (ri,vi)(r_i,v_i)(ri​,vi​) 的集合,1≤ri≤n,0≤∣vi∣≤1000,0001≤r_i≤n,0≤∣v_i∣≤1000,0001≤ri​≤n,0≤∣vi​∣≤1000,000

返回

从 1 到 n 每个结点的权值。

示例1

输入

5,[(2,5),(5,3),(5,4),(5,1)],2,[(1, 3), (2, -1)]

输出

[3,2,3,3,3]

说明

第一次操作,将以 1 为根节点的子树上的所有结点权值增加 3,此时结点的权值分别为 [3, 3, 3, 3, 3] ;
第二次操作,将以 2 为根节点的子树上的所有结点权值增加 -1,此时结点的权值分别为 [3, 2, 3, 3, 3] ;

解法:BFS

思路分析

本题难点在于:

  1. 输入中给的边是无向边,如何简便的分辨出父节点与子节点
  2. 如何在线性复杂度内计算出所有节点的权值

对于第一点,由于是树结构,遍历的时候肯定是从根节点往子节点遍历,因此可以用布尔数组 visited 记录访问过的节点,用列表数组 next记录节点 i 的相邻节点。若 next[i] 中的某节点 node 已访问,说明 nodei 的父节点。

对于第二点,用数组 ans 保存每个节点的权值:

  1. 先遍历一次询问集合 Query, 并仅记录子树根节点 rir_iri​ 的权值,不对其子树进行操作。
  2. 我们发现,节点 rir_iri​ 的最终权值 ans[ri]=ans[ri]+ans[fa(i)]ans[r_i] = ans[r_i] + ans[fa(i)]ans[ri​]=ans[ri​]+ans[fa(i)]。fa(i)fa(i)fa(i) 是 节点 rir_iri​ 的父节点。因此可以通过一次遍历完成权值的计算。

用 Java 的小伙伴们注意,树的遍历通常用 BFS 或 DFS。但是这里的节点是十万个,如果最坏情况下树退化成了单链表,使用 DFS 意味着需要十万个栈空间,显然会栈溢出。因此我们使用 BFS 来进行遍历。

时间复杂度:O(n)O(n)O(n)

空间复杂度:O(n)O(n)O(n)

代码实现

public long[] solve (int n, Point[] Edge, int q, Point[] Query) {long[] ans = new long[n];List<Integer>[] next = new List[n];boolean[] visited = new boolean[n];for(int i = 0; i < n; i++) next[i] = new LinkedList<Integer>();for(Point p: Edge){next[p.x - 1].add(p.y - 1);next[p.y - 1].add(p.x - 1);}for(Point p: Query) ans[p.x - 1] += p.y;Queue<Integer> queue = new LinkedList();queue.offer(0);while(!queue.isEmpty()){int root = queue.poll();visited[root] = true;for(Integer node: next[root]){if(visited[node]) continue;ans[node] += ans[root];queue.offer(node);}}return ans;
}

C. 旋转跳跃

题目描述

牛牛有一个长为 n 的排列 p ,与 m 对 (xi,yi)(x_i,y_i)(xi​,yi​).

每对 (xi,yi)(x_i,y_i)(xi​,yi​) 表示可以将 pxip_{x_i}pxi​​ 的值与 pyip_{y_i}pyi​​ 的值互换。

m 对 (xi,yi)(x_i,y_i)(xi​,yi​) 的使用顺序与次数不限。

牛牛想知道,任意次操作之后他能得到的字典序最小的排列是什么

字典序定义:对于数字 1、2、3…n 的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列 12354 和 12345,排列 12345 在前,排列 12354 在后。按照这样的规定,5 个数字的所有的排列中最前面的是 12345,最后面的是 54321。(来自百度百科)

输入

第一个参数为 n,1 ≤ n ≤ 100,000

第二个参数为 m,1 ≤ m ≤ 100,000

第三个参数为初始排列 p,1≤pi≤n1 ≤p_i≤n1≤pi​≤n

第四个参数为 m 对 (xi,yi)(x_i,y_i)(xi​,yi​), 1≤xi,yi≤n,xi≠yi1≤x_i,y_i≤n,x_i≠y_i1≤xi​,yi​≤n,xi​​=yi​

返回

字典序最小的排列

示例1

输入

5,3,[5,2,3,4,1],[(2,4),(1,4),(3,4)]

输出

[2,3,4,5,1]

说明

1. 交换 (3, 4), 交换后的序列为: 5, 2, 4, 3, 1
2. 交换 (2, 4), 交换后的序列为: 5, 3, 4, 2, 1
3. 交换 (1, 4), 交换后的序列为: 2, 3, 4, 5, 1

解法:并查集

思路分析

把下标看作图中的节点, (xi,yi)(x_i,y_i)(xi​,yi​) 表示节点 pxip_{x_i}pxi​​ 和 pyip_{y_i}pyi​​ 直接相连。由于 m 对 (xi,yi)(x_i,y_i)(xi​,yi​) 的使用顺序与次数不限,因此同一连通分量中的节点可以任意排列。

例如:对原排列 a, b, c, 给定 (a, b), (b, c) ,我可以排列成 b, a, c(仅用到 (a, b)),也可以排列成 a, c, b(仅用到 (b, c))。

显然使用并查集可以方便的得到所有的连通分量。之后对每一个连通分量内部进行排序,小的值放在小的下标上。

时间复杂度:O(nlogn)O(nlogn)O(nlogn).

空间复杂度:O(n)O(n)O(n).

代码实现

class Solution {public int[] solve (int n, int m, int[] perm, Point[] Pair) {int[] ans = new int[n];Union un = new Union(n);for(Point p: Pair) un.union(p.x - 1, p.y - 1);Map<Integer, Queue<Integer>> valueMap = new HashMap();for(int i = 0; i < n; i++){int fa = un.find(i);if(!valueMap.containsKey(fa)) valueMap.put(fa, new PriorityQueue());valueMap.get(fa).add(perm[i]);}for(int i = 0; i < n; i++){Queue<Integer> valueSet = valueMap.get(un.find(i));ans[i] = valueSet.poll();}return ans;}
}class Union{private int[] parent;public Union(int size){parent = new int[size];for(int i = 0; i < size; i++) parent[i] = i;}public int find(int x){if(x != parent[x]) parent[x] = find(parent[x]);return parent[x];}public void union(int x, int y){parent[find(x)] = find(y);}
}

写在最后

大家好,我是往西汪,一位坚持原创的新人博主。
如果本文对你有帮助,请动动小手指点个赞

算法题解 - 牛客编程巅峰赛S1第3场 - 黄金钻石组相关推荐

  1. 牛客编程巅峰赛S1第6场 - 黄金钻石王者(总结)

    牛客编程巅峰赛S1第6场 - 黄金&钻石&王者(总结) A:牛牛爱奇数 题意 有一个由n个元素组成的数组,牛牛想要将所有的数都变成奇数(即:将所有的偶数都变成奇数),但是他的操作是:一 ...

  2. 牛客编程巅峰赛S1第7场 - 黄金钻石A-B-C

    比赛链接:牛客编程巅峰赛S1第7场 - 黄金&钻石 文章目录 A.牛牛打怪兽 DFS B.牛牛的冰激凌 贪心 C.数列求值 矩阵快速幂 A.牛牛打怪兽 DFS 题意 身为屯里第一剑士的牛牛来到 ...

  3. 牛客编程巅峰赛S1第6场 - 黄金钻石王者题解

    牛牛爱奇数 链接:https://ac.nowcoder.com/acm/contest/6629/A 来源:牛客网 题目描述 在牛牛面前放着n个数,这些数字既有奇数也有偶数,只不过牛牛对奇数情有独钟 ...

  4. 牛客编程巅峰赛S1第2场 - 黄金钻石 1.规律 2.bfs

    链接:https://ac.nowcoder.com/acm/contest/6357/A 来源:牛客网 题目描述 牛牛重新定义了斐波那契数列,牛牛定义f(n) = f(n-1)+f(n+1); f( ...

  5. 牛客编程巅峰赛S1第3场 - 黄金钻石 A.简单题 B.dfs C.并查集

    链接:https://ac.nowcoder.com/acm/contest/6383/A 来源:牛客网 找卧底 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语 ...

  6. 牛客编程巅峰赛S1第7场 - 黄金钻石 A.dfs B.dpC.快速幂

    链接:https://ac.nowcoder.com/acm/contest/6631/A 来源:牛客网 题目描述 题意 身为屯里第一剑士的牛牛来到训练场里闯关,由于过于勤奋,牛牛的宝剑的耐久度降到了 ...

  7. 牛客编程巅峰赛S1第6场 - 黄金钻石王者 C.dijkstra

    链接:https://ac.nowcoder.com/acm/contest/6629/C 来源:牛客网 题目描述 牛牛和牛妹在进行一场星球模拟游戏,游戏规则如下: 游戏地图内共有n个星球,共有m条隧 ...

  8. 牛客编程巅峰赛S1第2场 - 青铜白银

    A.牛牛扔牌 链接:https://ac.nowcoder.com/acm/contest/6219/A 来源:牛客网 题目描述 牛牛现在有n张扑克牌,每张扑克牌都有点数和花色两部分组成.点数为'1' ...

  9. 牛客编程巅峰赛S2第5场 - 钻石王者 C.Tree III

    牛客编程巅峰赛S2第5场 - 钻石&王者 C.Tree III 题目链接 题目描述 给出一棵有n个节点的节点标号为1~n的有根树(根为第一个节点,并给出从第2个节点到第n个节点的父结点),请你 ...

最新文章

  1. angularJs 之deferred
  2. 深度丨如何理解和评价机器学习中的表达能力、训练难度和泛化性能
  3. loss=nan解决办法
  4. EditPlus3.21注册码
  5. shell 删除奇数偶数行
  6. Android ViewPager
  7. linux安装mysql5.6
  8. 栅格矢量化_学会用栅格系统,普通LOGO秒变高大上
  9. 解决 VUE:[WDS] Errors while compiling. Reload prevented...- invalid expression: Unexpected token -- in
  10. 另一个.NET上的文档数据库Raven[转载]
  11. Oracle客户端tnsnames.ora连接配置
  12. Greenplum:你不可不知的实施与维护最佳实践
  13. 数据库c3p0配置SQL Server与MySQL
  14. 百战程序员数据结构 课件_结构之战
  15. 无密码,撤销excel工作表保护
  16. 宏文件下载_新星邮件速递专家2021免费版-新星邮件速递专家2021正式版下载
  17. SpringBoot整合使用XXL-JOB
  18. Python人工智能基础到实战课程-北方网视频
  19. java8获取本周本月第一天和最后一天
  20. 解决mini_httpd_v1.30在使用http post请求出现 socket hang up的问题

热门文章

  1. Python语言程序设计(嵩天老师版)第一周笔记
  2. 数据中心电气设计及审图的注意事项(三)
  3. 齐岳含有EGS交联剂|乙二醇双(琥珀酰亚胺基琥珀酸酯)|70539-42-3 蛋白交联剂
  4. Android N编译之Out of memory error
  5. 作业调度和进程调度的辨析
  6. 连续论域模糊控制,博途1200程序代码
  7. 小眼睛适合大框还是小框眼镜_近视眼镜大框好还是小框好?
  8. Moveit!入门——古月居机械臂开发笔记(一)
  9. JAVA+网络数据包的抓取与分析实验
  10. slam打脸入门知识