题目描述
牛牛有一个算法交流群,它是这个群的群主,也是这个群实力最强的人。
算法交流群里一共有n个人,每个人都有一个等级ai表示它能解决难度小于等于ai的算法问题。
除了牛牛以外,群里的每个编号为i的人都在群里有一个等级比他高的朋友编号为pi。群友 i 会解决那些他产生和接收的等级小于等于ai的问题,并把解决不了的问题全部交给pi。
保证牛牛的编号为1。保证牛牛的等级全场唯一且全场最高。如果牛牛解决不了他接收的问题,他将不管这些问题。
这天群里的每个人都产生了一个问题,牛牛知道了每个人产生问题等级ki,他想知道群里的每个人在这天解决了多少问题。
方法一:暴力解法
求解思路
对于本题目的求解,我们可以把牛牛看作树的根节点,其余群友看作树的枝叶,如果某一位群友不能解决问题,那么就顺着这个群友向上寻找等级比他高的人,也即寻找该节点的父节点。并且同时我们用一个数组来记录问题最终是被哪个群友解决的,基于上述思路,我们便可以得到本问题的答案。
解题代码
class Solution:
def solve(self , n , a , p , k ):
myres = [0]*n # 返回数组
dic ={} # 存储每个人i遇到等级v时候最终对应的解决人是谁
for i,v in enumerate(k): # 遍历
if a[i]>=v: # 如果能力值大于问题等级
dic[(i,v)] = i
myres[i]+=1
else:
t = i
while t!=0 and a[i]<v:
t = p[t-1] -1
if (t,v) in dic:
myres[dic[(t,v)]]+=1
break
if a[t]>=v: # 如果 t的能力值大于问题等级则加入字典
dic[(i,v)]=t
myres[t]+=1
break
return myres # 返回结果即可复杂度分析
时间复杂度:一层循环,因此时间复杂度为
空间复杂度:使用辅助数组dic[N]来记录求解问题的人,引入额外的内存地址空间,因此空间复杂度为
方法二:优化解法
求解思路
对于本题的解法,我们可以对其做出优化。因为牛牛的等级为最高,我们给牛牛一个权值1,其在[0,1]权值中为最大。我们发现从根到结点的路径上,点的权值与点到根的距离有关,距离越远其相应的权值越小,并且满足二分性质。根据上述思路,我们使用类二分的方法对本题进行解决。
解题代码
class Solution {
public:
int f[100005][20];
vector<int> g[100005];
void dfs(int u)
{ //定义搜索方式
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)
{
vector<int> myans(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]) myans[0]++;
for(int i = 1; i < n; ++i)
{
if(k[i] <= a[i]) // 如果满足条件,则myans++
{
myans[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 myans[u-1]++;
}
return myans; // 返回结果即可
}
};复杂度分析
时间复杂度:一层循环,因此时间复杂度为
空间复杂度:使用常数级内存地址空间,因此空间复杂度为

京公网安备 11010502036488号