强迫症的序列 牛客网 思维

强迫症的序列 牛客网 思维

题意

链接:https://ac.nowcoder.com/acm/contest/90/J
来源:牛客网。
小A是一个中度强迫症患者,每次做数组有关的题目都异常难受,他十分希望数组的每一个元素都一样大,这样子看起来才是最棒的,所以他决定通过一些操作把这个变成一个看起来不难受的数组,但他又想不要和之前的那个数组偏差那么大,所以他每次操作只给这个数组的其中n-1个元素加1,但是小A并不能很好的算出最优的解决方案,如果你能帮他解决这个问题,小A就能送你一个气球

输入:
第一行一个整数T(T<=100),表示组数
对于每组数据有一个n,表示序列的长度(0< n <100000)
下面一行有n个数,表示每个序列的值(0<(ai)<1000)

输出:
两个数
第一个数表示最小的操作步数
第二个数经过若干步以后的数组元素是什么

例如:
1
3
1 2 3
输出:
3 4

解题思路

这里有一个很奇怪的点,就是每次操作都需要选择(n-1)个数,然后对每个数都加1。这样我们可以反过来想:相当于选择一个数进行减一,然后使得所有的数都相同,这里很容易想到,既然是只能选择一个数进行减一操作,那我就只能把所有的数都减到这(n)个数中的最小值喽,因此我们就计算出了总的操作次数,即每个数和最小数的差的和。公式如下:(a)代表操作的次数。

[a=sum_{i=0}^{n-1}(a[i]-min) quad ext{min代表最小值,数组从下标0开始存储} ]

这里操作次数计算出来了,但是最后我们把这些数实际变成了什么呢?也就是需要输出的第二个数。这里我们可以简单发现下面的公式,假设(b)就是需要输出的第二个数。

[egin{aligned}underbrace{a*(n-1)+sum}_ ext{这里是a次操作后,这n个数的总和}=underbrace{n*b}_ ext{这里也是a次操作后的n个数的总和} quad ext{:sum代表这n个数原来的总和}end{aligned} ]

这样我们就可以根据上面的公式就可以计算第二个需要的数(b)

[egin{aligned}b& = frac{a*(n-1)+sum}{n} \& = frac{a*n+sum-a}{n} \& = a+frac{sum-a}{n}end{aligned} ]

代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1e5+7;
const int inf=0x3f3f3f3f;
int num[maxn];
int t, n, sum;
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        sum=0;
        int a=0, sum=0, minn=inf;
        for(int i=0; i<n; i++)
        {
            scanf("%d", &num[i]);
            minn=min(minn, num[i]);
            sum+=num[i];
        }
        for(int i=0; i<n; i++)
            if(num[i]>minn)
                a+=num[i]-minn;
        printf("%d %d
", a, a+(sum-a)/n);//注意第二个表达式,这里要避免进行
    }
    return 0;
 }
欢迎评论交流!
原文地址:https://www.cnblogs.com/alking1001/p/11953745.html