- 在传球游戏中最大化函数值
提示
困难
27
相关企业
给你一个长度为 n 下标从 0 开始的整数数组 receiver 和一个整数 k 。
总共有 n 名玩家,玩家 编号 互不相同,且为 [0, n - 1] 中的整数。这些玩家玩一个传球游戏,receiver[i] 表示编号为 i 的玩家会传球给编号为 receiver[i] 的玩家。玩家可以传球给自己,也就是说 receiver[i] 可能等于 i 。
你需要从 n 名玩家中选择一名玩家作为游戏开始时唯一手中有球的玩家,球会被传 恰好 k 次。
如果选择编号为 x 的玩家作为开始玩家,定义函数 f(x) 表示从编号为 x 的玩家开始,k 次传球内所有接触过球玩家的编号之 和 ,如果有玩家多次触球,则 累加多次 。换句话说, f(x) = x + receiver[x] + receiver[receiver[x]] + … + receiver(k)[x] 。
你的任务时选择开始玩家 x ,目的是 最大化 f(x) 。
请你返回函数的 最大值 。
注意:receiver 可能含有重复元素。
示例 1:
传递次数 传球者编号 接球者编号 x + 所有接球者编号
2
1 2 1 3
2 1 0 3
3 0 2 5
4 2 1 6
输入:receiver = [2,0,1], k = 4
输出:6
解释:上表展示了从编号为 x = 2 开始的游戏过程。
从表中可知,f(2) 等于 6 。
6 是能得到最大的函数值。
所以输出为 6 。
示例 2:
传递次数 传球者编号 接球者编号 x + 所有接球者编号
4
1 4 3 7
2 3 2 9
3 2 1 10
输入:receiver = [1,1,1,2,3], k = 3
输出:10
解释:上表展示了从编号为 x = 4 开始的游戏过程。
从表中可知,f(4) 等于 10 。
10 是能得到最大的函数值。
所以输出为 10 。
提示:
1 <= receiver.length == n <= 105
0 <= receiver[i] <= n - 1
1 <= k <= 1010
通过次数
2.2K
提交次数
5.7K
通过率
38.5%
准备工作:记录数组长度,记录k的二进制数长度,记录此长度主要用于后续构建二维数组,即统计从任意球员开始,该球员传递2 ^ i(i = 0,1,2,…)次方后球到达的位置,以及经过传递后,球经过为所有球员编号的和;
精髓:该方法中主要使用递归实现减少程序的时间复杂度,即在准备工作中谈到的两种数据在计算时可使用初始数据依次得出,具体见代码
准备工作完成之后,在进行暴力枚举时时间复杂度已符合相关要求
Java代码
class Solution_2836 {
/**
* 阶解题思路:先计算得出k的二进制数长度,然后遍历receiver,
* 枚举出每一个节点传递2^j次方求后的最终节点以及传递过程中接球人的和,
* 最后枚举出以每一个球员开球情况下的总和,并返回最大值
* @param receiver
* @param k
* @return
*/
public long getMaxFunctionValue(List<Integer> receiver, long k) {
int m = 64 - Long.numberOfLeadingZeros(k); // 记录k的二进制数长度
long ans = 0; // 记录返回值
int len = receiver.size(); // 记录receiver长度
int [][] pa = new int[len][m]; // 用于记录每一个结点开始,传2 ^ j次球后,球所处的位置
long [][] sum = new long[len][m]; // 记录传2 ^ j次球过程中累加到的编号和
for (int i = 0; i < len; i++) {
pa[i][0] = receiver.get(i); // 以i为初始节点,传 2 ^ 0次方球时球的落点位置
sum[i][0] = receiver.get(i); // 以i为初始节点,传 2 ^ 0次方时累加到的编号和
}
for (int i = 0; i < m - 1; i++) {
for (int j = 0; j < len; j++) {
int flag_index = pa[j][i];
pa[j][i + 1] = pa[flag_index][i]; //将持球人信息更新到传球之后
sum[j][i + 1] = sum[j][i] + sum[flag_index][i];
//sum[j][i]统计的是从当前球员j出发,传递2 ^ i次方后增加的编号和,
//sum[flag_index][i]统计的是传递2 ^ i次方后到达的位置,到达该位置后记录从该位置传递2 ^ i次方次后增加的编号和
//两者相加即为从j球员开始,传递2 ^ (i + 1)次方后总共的编号和
}
}
for (int i = 0; i < len; i++) { //枚举出每一个球员作为第一站时,k次传球后的累加和,同时找出最大值
long count = i; //直接加上第一开球人编号
int index = i; //记录当前持球人员编号
for (int j = 0; j < m; j++) {
if (((k >> j) & 1) == 1) {
count += sum[index][j];
index = pa[index][j];
}
}
ans = ans > count ? ans : count;
}
return ans;
}
}
该程序时间复杂度为 : n + n * log(k) + n * log(k) = n * log(k)