[LeetCode 1671.] 玩游戏

1671. 玩游戏

CAT 专属题目 hard 难度

题目描述

N 个人在玩游戏,每局游戏有一个裁判和 N-1 个平民玩家。给出一个数组 A, A[i] 代表玩家 i 至少需要成为平民 A[i] 次,返回最少进行游戏的次数。

样例

样例 1:

输入:A = [2, 2, 2, 2]
输出:3
解析:
A[0] = 2表示玩家0至少需要成为2次平民
第一局:玩家0担任裁判,此时 A[0] = 0, A[1] = 1, A[2] = 1, A[3] =1
第二局:玩家1担任裁判,此时 A[0] = 1, A[1] = 1, A[2] = 2, A[3] = 2
第三局:玩家2担任裁判,此时 A[0] = 2, A[1] = 2, A[2] = 2, A[3] = 3
此时每个玩家都达到了要求,所以进行三局游戏即可

样例 2:

输入:A = [84,53]
输出:137
解析:
第一局:玩家1担任裁判 ,此时 A[0] = 1, A[1] = 0
......
第三十一局:玩家1担任裁判,此时 A[0] = 31, A[1] = 0
第三十二局:玩家0担任裁判,此时 A[1] = 31, A[1] = 1
第三十三局:玩家1担任裁判,此时 A[1] = 32, A[1] = 1
第三十四局:玩家0担任裁判,此时 A[1] = 32, A[1] = 2
......
第一百三十七局:玩家1担任裁判,此时 A[1] = 84, A[1] = 53
此时每个玩家都达到了要求,所以进行一百三十七局游戏即可
注意事项

  • ∑Ai<=1e18
  • 1 < n < 1000

解题思路

想法1:从后往前逆推,从现有状态猜测上一轮的裁判是谁。
每一轮都让分数最低的人当裁判,这样高分选手才能尽快降下来。
但是每一轮都找最低分,然后给其余元素更新,时间复杂度O(n*MAX)太高。

想法2:分段解决。
先把最高分的分数凑出来。然后看其余人有没有没满足的,再补上不够的分数。
关键的问题是,怎么判定阈值是多少?怎么给出应该补多少分?

凑够最高分需要 MAX 轮,n个人总共得分 (n-1)*MAX 分。如果总得分少于这个数,则可以凑出来,凑分方案如下:

从前往后推。每次选取分数最高的人做裁判,如果都相等则任取一人做裁判。这样n个人从全部为0分到最高为MAX分,所需轮数是MAX轮。
注意,因为每次选分数最高的做裁判,所以这n个人的分数相差不会超过1。

逆向操作,从后往前推。这里直接对于最高分之外的其余人,各自减去最高分,这样就都变成了负分。

  • 如果这个负数的绝对值不少于最高分,那么这意味着这n-1个人的分数少于凑最高分所需分数,凑最高分的过程中就能满足这些人的分数需求。
  • 如果这个负数的绝对值小于最高分,那么还需要填补这些分数,这几轮让最高分来担任裁判,这n-1个人一起加分。

参考代码

class Solution {
public:
    /**
     * @param A: 
     * @return: nothing
     */
    long long playGames(vector<int> &A) {
        // Write your code here
        std::sort(A.begin(), A.end());
        size_t n = A.size();
        int maxA = A[n-1];
        long long sum = 0LL;
        long long res = 0LL;
        for (size_t i=0; i<n-1; i++) {
            A[i] -= maxA;
            sum += A[i];
        }
        if (sum + maxA <= 0) {
            return maxA;
        } // 前n-1个人轮流做裁判就够了
        return ceil((double)(sum + maxA) / (n - 1)) + maxA; //还需要补几轮,前n-1个人均摊一下
    }
};
原文地址:https://www.cnblogs.com/zhcpku/p/14259486.html