解法:
假设编号从左到右递增,奶牛每次只能去往左边的牛圈。因此等分最大奶牛数小于等于最右边牛圈奶牛数,不妨设数为k,那么a[i]>=k,a[i-1]>=2k。。。
做后缀和+二分答案就可找到k
补充一下思路:
1.模拟验证答案的可行性
假设有4个牛圈,a1,a2,a3,a4.答案为k,a4一定大于等于k,多的奶牛进入a3,然后a3一定大于等于k,注意a3加上了a4多的奶牛,直到a1中多的奶牛离开牛圈。可以发现a4中的奶牛进入a3后,a3,a4牛圈的和还是不变,这里采用等差数列求和的方式,从而从一个一个与k比较转化为该项对应的前n项和和n*k比较。
2.枚举可以均分奶牛的答案,并取最大值
k=0;while (check(k++))去不断试答案,直到取最大值。
使用二分只是优化试答案这个过程
3.用最大值去模拟奶牛进入下一个牛圈的过程并记录操作数。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define endl '\n'
bool check(vector<int>& ne, int k) {
int n = 1;
for (int i = ne.size()-2; ~i; i--) {
if (ne[i] < n * k) {
return false;
}
else n++;
}
return true;
}
int main() {
int n; cin >> n;
vector<int> vec(n+1), ne(n+1);
for (int i = 0; i < n; i++) cin >> vec[i];
for (int i = n - 1; ~i; i--) ne[i] = vec[i] + ne[i + 1];
int l = 0, r = vec[n - 1];
int res = 0;
while (l <= r) {
int mid = l + (r - l) / 2;
if (check(ne, mid)) {
res = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
long long sum = 0;
for (int i = n - 1; ~i; i--) {
if (vec[i] > res) {
if (i > 0) {
vec[i - 1] += vec[i] - res;
}
sum += vec[i] - res;
}
}
cout << sum << endl;
return 0;
}
解法二:
#include <iostream>
using namespace std;
int a[100005], b[100005];
int main()
{
long long n, num = 0, min = 1e9;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = n; i >= 1; i--)
b[i] = a[i] + b[i + 1];
for (int i = 1, j = n; i <= n; i++, j--) {
if ((b[i] / j) < min) min = (b[i] / j);
}
for (int i = 1, j = n; i <= n; i++, j--) {
num += abs(b[i] - min * j);
}
cout << num;
}
解法三:
#include<iostream>
#define int long long
using namespace std;
int a[100005];
signed main()
{
int n, sum = 0, m, p, cnt = 0, num = 0;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
sum += a[i];
}
m = sum % n;
p = (sum - m) / n;
for (int i = n - 1; i >= 0; i--)
{
if (i == 0 && a[i] > p)cnt += (a[i] - p);
else if (a[i] > p)cnt += (a[i] - p), a[i - 1] += (a[i] - p), a[i] = p;
else if (a[i] < p)p --,i = n;
}
cout << cnt;
}