算法思想一:普通解法
解题思路:
除了群主牛牛,每个群友都有一个等级大于他的人作为朋友,那么最终是一个以牛牛为根的树。所以对于每个群友,一直往上找到第一个等级足够处理这个群友产生的问题上级,并统计答案。
在此基础上加一个字典,缓存每个人i遇到等级v时候最终对应的解决人是谁,下次遇到就不用递归去找了
流程:
1、初始化返回数组 res,字典 dic:缓存每个人i遇到等级v时候最终对应的解决人是谁
2、遍历产生的问题 k 数组:
1、如果当前人的能力值大于问题等级,则将 其存入字典中记录
2、否则,进行循环:当不是群主且当前人的能力值小于问题等级
1、找到此题应该寻找的朋友 t = p[t-1] -1
2、判断这个朋友做的题等级记录是否在字典中,若在,则此人做题数量加1
3、如果当前人能力值大于问题等级,则继续加入字典
3、最后返回 res
图解:
代码展示:
# # 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 # # 算法交流群 # @param n int整型 群员个数 # @param a int整型一维数组 群员的等级 # @param p int整型一维数组 群友寻求帮助的人 # @param k int整型一维数组 群友产生的问题等级 # @return int整型一维数组 # class Solution: def solve(self , n , a , p , k ): # write code here # 返回数组 res = [0]*n # 字典 缓存每个人i遇到等级v时候最终对应的解决人是谁 dic ={} # 遍历 for i,v in enumerate(k): # 能力值大于问题等级 存入字典 if a[i]>=v: dic[(i,v)] = i res[i]+=1 else: t = i # 不是群主 且 能力值小于问题等级 while t!=0 and a[i]<v: # 寻找朋友 t = p[t-1] -1 # 如果 t 对应的题在字典中,则此人的题数加1 if (t,v) in dic: res[dic[(t,v)]]+=1 break # 如果 t的能力值大于问题等级,继续加入字典 if a[t]>=v: dic[(i,v)]=t res[t]+=1 break return res
复杂度分析
时间复杂度O(N):N表示问题数量,依次遍历时间
空间复杂度O(N):字典占用空间
算法思想二:最优解法
解题思路:
因为牛牛的编号是1且它全场最高。整棵树是以1为根的有根树。一个结点的权值一定大于它的子孙结点权值。整棵树呈堆的形式。因为从根到一个结点的路径上,点的权值是越远离根结点,权值越小,满足二分性质。
树上倍增求得每个点向上 2^i 层的祖先。求法为:用 f[u][i] 表示结点 u 向上跳 2^i 层可以到达的结点。
然后在上面用类似二分的方法找到第一个能解决当前问题的祖先。
代码展示:
C++版本
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 算法交流群 * @param n int整型 群员个数 * @param a int整型vector 群员的等级 * @param p int整型vector 群友寻求帮助的人 * @param k int整型vector 群友产生的问题等级 * @return int整型vector */ int f[100005][20]; vector<int> g[100005]; void dfs(int u){ // cout<<"u:"<<u<<endl; for(int i = 0; i < 19; ++i) f[u][i+1] = f[f[u][i]][i]; for(int i = 0; i < g[u].size(); ++i) dfs(g[u][i]); } vector<int> solve(int n, vector<int>& a, vector<int>& p, vector<int>& k) { // write code here vector<int> ans(n, 0); for(int i = 0; i <= n; ++i) g[i].clear(); memset(f, 0, sizeof f); for(int i = 0; i < n-1; ++i) f[i+2][0] = p[i],g[p[i]].push_back(i+2) ; dfs(1); if(k[0] <= a[0]) ans[0]++; for(int i = 1; i < n; ++i){ if(k[i] <= a[i]){ans[i]++; continue;} int u = i+1; for(int j = 18; j >= 0; --j){ if(f[u][j] == 0) continue; int t = f[u][j]; if(k[i] > a[t-1]) u = t; } u = f[u][0]; if(u == 0) continue; else ans[u-1]++; } return ans; } };
复杂度分析
时间复杂度O(NlogN):N表示问题数量,一层遍历再加一层二分统计时间
空间复杂度O(NlogN):需要额外的空间存储倍增数组