思路:
题目的主要信息:
- 三个数组,a表示每个人能够解决的问题的最大等级(首位必然最大),p为除a的首位外每个人拥有的能比自己能够解决更大等级问题的朋友,自己不能解决的问题可以交给朋友,k表示每个人产生的问题等级。
- 当某个人产生一个问题,自己能够解决就自己解决,自己不行就交给朋友,朋友不行再给朋友的朋友,依次类推,直到a的首位不能解决,则放弃
- 求a中每个人解决的问题数
关键点:谁产生的问题,谁优先解决,不行再往更高处交移
方法一:暴力法
具体做法:
遍历问题,对于每个问题,优先给自己解决,不行交给朋友,朋友不行再交给朋友的朋友,直到推到首位,再检查首位能不能解决。
class Solution { public: vector<int> solve(int n, vector<int>& a, vector<int>& p, vector<int>& k) { vector<int> res(n, 0); for(int i = 0; i < n; i++){ if(k[i] <= a[i]){ //自己可以解决 res[i]++; continue; } else if(i == 0) //牛牛不能解决 continue; int temp = p[i - 1]; //朋友的序号,不是下标,所以下面都要多减1 while(temp != 1){ if(a[temp - 1] >= k[i]){ //朋友解决 res[temp - 1]++; break; } temp = p[temp - 2]; } if(temp == 1 && a[0] >= k[i]) //牛牛解决 res[0]++; } return res; } };
复杂度分析:
- 时间复杂度:,最坏情况每个都要牛牛解决或者牛牛都不能解决,则需要遍历两层
- 空间复杂度:,常数空间,其中res是返回函数必要空间,不算额外空间
方法二:树+递归
具体做法:
因为首位牛牛的编号是最高的,它是没有求助的朋友的,而比它次一点的一个或者多个节点的朋友是它,再依次往下推。那么,我们可以把这种朋友关系看成是树的父子关系,孩子节点解决不了的问题,我们往上找父节点,最终到根节点,也就是牛牛,我们以某个解决能够解决的最大问题作为树的权值,那么该树一定是大顶堆型的树。
因为从根到一个结点的路径上,点的权值是越远离根结点,权值越小,因此满足二分性质。树上倍增求得每个点向上层的祖先,我们可以用表示结点向上跳层可以到达的结点,在建树的时候就把这个数组完善。
class Solution { public: void dfs(int u, vector<vector<int>>& g, vector<vector<int>>& f){ //建立树 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], g, f); //递归 } vector<int> solve(int n, vector<int>& a, vector<int>& p, vector<int>& k) { vector<int> res(n, 0); vector<vector<int> > f(n + 1, vector<int>(20)); //最多2^19层 vector<vector<int> > g(n + 1); //记录对于每一层的子节点 for(int i = 0; i < n - 1; i++){ f[i + 2][0] = p[i]; g[p[i]].push_back(i + 2); } dfs(1, g, f); //深度优先建树 if(k[0] <= a[0]) //首位先验算 res[0]++; for(int i = 1; i < n; i++){ if(k[i] <= a[i]){ //自己解决 res[i]++; continue; } int temp = i + 1; for(int j = 18; j >= 0; j--){ //向上找 if(f[temp][j] == 0) continue; int t = f[temp][j]; if(k[i] > a[t - 1]) temp = t; } temp = f[temp][0]; if(temp == 0) //牛牛不能解决 continue; else res[temp - 1]++; //最近的解决位 } return res; } };
复杂度分析:
- 时间复杂度:,每次从一个节点到根节点最多为,一共次
- 空间复杂度:,建树时递归栈的最大空间