LeetCode 2836. 在传球游戏中最大化函数值

  1. 在传球游戏中最大化函数值
    提示
    困难
    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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值