问题:
已知, 都是不超过int的正整数,给两个操作:操作1: 可以将任意一个数加1并消耗C1金币。操作2:可以将任意两个数分别加1,并消耗C2金币。现在问最少消耗多少金币能够让A中所有数相等。(n <= 10^6),结果对1000000007取模。
思路:
我们记A中最小元素为,最大元素为
1.如果c2 >= 2*c1 那说明无论如何用操作1都比较划算。
用操作1将A中元素全部加到,就是答案。
记要加的数和为,答案是
2.如果c2 < 2*c1,
我们第一步要尽可能用操作2将A中元素全部加到,这样比较划算
如果 ,那么用操作2一直操作最小的那个数和其他小于的数,最后A会变成n-1个, 和1个
如果, 那么如果是偶数,一定可以用操作2将A中所有数加到;那么如果是奇数,可以用操作2将A中n-1个数加到,1个数加到。
下面讨论对最后1个数(即第一种情况的和第二种情况的)怎么办。假设最后一个数还剩r就到(即最后一个数等于)。
那么假设最后加到所有数为k+, 需要x次操作1和y次操作2,可以得到以下约束条件;
最小化代价:min(x*c1+y*c2), y = k*(n-1), k*(n-1)+x = r+k
整理得:需要最小化 min(-k*(n-2)*c1+k(n-1)*c2) = min(r*c1+((n-1)c2-(n-2)c1)k)
如果(n-1)c2>(n-2)c1 k取0最小
如果(n-1)c2<=(n-2)c1 根据这个式子r=k(n-2)+x,k最大取r/(n-2)
代码:
int solution(vector<int>& A, int c1, int c2) {
if (A.size() == 0) {
return 0;
}
LL res = 0;
int maxval = 0;
for (int it: A) {
maxval = max(it,maxval);
}
if (c2 >= 2*c1) {
for (auto& it: A) {
res = (res + (maxval - it)*(LL)c1 % mod) %mod;
}
return res;
} else {
int maxr = 0;
LL totr = 0;
for (auto it:A) {
maxr = max(maxval-it,maxr);
totr += maxval-it;
}
int r;
if (maxr > totr-maxr) {
r = maxr-totr+maxr;
res = (res + (LL)c2*(totr-maxr))%mod;
} else {
r = totr%2;
res = (res + (LL)c2*((totr/2)%mod))%mod;
}
int n = A.size();
if (n<=2) {
return (res + r*(LL)c1%mod)%mod;
} else {
if ((n-1)*(LL)c2 > (n-2)*(LL)c1) {
return (res+r*(LL)c1%mod)%mod;
} else {
int k=r/(n-2);
int x=r%(n-2);
return (res+(n-1)*(LL)k*c2%mod + x*(LL)c1%mod)%mod;
}
}
}
return 0;
}