题目链接
题意:
给n个数,有三种操作:
1.任选一个数+1 花费为a
2.任选一个数-1 花费为b
3.任选两个数,一个-1,一个+1 花费为c
问至少花费多少能将这n个数变成一样大的 每个数都要大于等于0
数据范围:
1≤n≤1e5, 0≤a,b,c≤1e4
0≤a[i]≤1e9
思路:
首先需要想到最终变成的数肯定在0~1e9之间
然后 在某个数时花费存在最小值,其他的数离这个数越远,花费就越大
然后就变成了一个凹函数 找极小值 用三分法
对于某个数y 判断所有数变成这个数的花费的方法:
统计需要+1的次数minn和需要-1的次数maxx
if (c>a+b) 花费为:a * minn+b * maxx
else if (minn < maxx) 花费为 :c * minn + b * (maxx - minn);
else 花费为 c * maxx + a * (minn - maxx);
三分补充:
lmid = l + (r - l) / 3;
rmid = r - (r - l) / 3;
if (lmid对应的值 比 rmid对应的值更接近所求)
r = rmid;
else
l = lmid;
因为这里是整数三分,所以l和r都是整数
显然当r-l<3的时候 lmid和rmid将不再改变
所以while的条件应为r-l≥3
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x[100005];
ll n, a, b, c, i, j;
ll cheak(ll y)
{
ll minn = 0, maxx = 0;
for (i = 1; i <= n; i++)
{
if (x[i] < y)
minn += y - x[i];
if (x[i] > y)
maxx += x[i] - y;
}
if (a + b <= c)
return a * minn + b * maxx;
else if (minn < maxx)
return c * minn + b * (maxx - minn);
else
return c * maxx + a * (minn - maxx);
}
int main()
{
scanf("%lld%lld%lld%lld", &n, &a, &b, &c);
for (i = 1; i <= n; i++)
scanf("%lld", &x[i]);
sort(x + 1, x + n + 1);
ll l = 0, r = 1e9 + 5, ans = 1e18;
while (r - l >= 3)
{
ll lmid = l + (r - l) / 3;
ll rmid = r - (r - l) / 3;
if (cheak(lmid) < cheak(rmid))
r = rmid;
else
l = lmid;
}
ans = min(ans, cheak(l));
ans = min(ans, cheak(l + 1));
ans = min(ans, cheak(l + 2));
printf("%lld\n", ans);
}