算法思想一:普通解法
解题思路:
除了群主牛牛,每个群友都有一个等级大于他的人作为朋友,那么最终是一个以牛牛为根的树。所以对于每个群友,一直往上找到第一个等级足够处理这个群友产生的问题上级,并统计答案。
在此基础上加一个字典,缓存每个人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):需要额外的空间存储倍增数组



京公网安备 11010502036488号