强迫症的序列 牛客网 思维
题意
链接: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;
}