均衡操作2
小J面前有N桶水,每个桶装的水的体积不一样
现在小J希望让所有桶的水的体积变得一样
每次他会选择两个相邻的桶子, 将桶中的水都倒掉1个单位
问他至少倒掉多少水,使得所有桶的水一样多,如果达不到目的,输出-1
Format
Input
第一行给出数字T,代表数据的组数
对于每组数据,先给出N 下面一行给出N个数字,代表每个桶的水的体积
N<=1e5
体积<=1e9
Output
如题
Samples
输入数据 1
2
3
8 10 5
6
4 6 4 4 6 4
输出数据 1
14
16
Hint
第1次,将第2个与第3个桶子都倒掉2个单位的水
第2次,将第1个与第2个桶子都倒掉5个单位的水
Sol:我们先来看样例的操作过程
既然要让所有数字变得一样,那当然要找到最大的那个数字10,让10与5一起减少2,这样10就变成了8
与左边的8变成一样。于是变成
8 8 3
然后再将最左边两个数字8 8一起减少5,得到 3 3 3.
此时我们会发现,如果每次在要数列中找最大值来减,是个比较麻烦的事。
又发现,在整个操作中,只要最终达到目的,先减哪个,再减哪个,并不重要。。。。
于是仍以样例来说
8 10 5
我们的目标是让所有数字变成一样
于是对于10来说,它比左边的8大,于是10就要变小,而按题意,相邻的两个数字都要变
于是10就拉上右边的5一起变,变成
8 8 3
通过这样的变化,我们能使整个数列的第1个数字到第N-1数字,它们会变成一个不上升的数列(当然最理想的状态是前N-1个数字变得一样了)
接下来,我们可以将8 8 3倒过来得到3 8 8
再按上面的思路做一遍得到3 3 3
当然这个操作过程中还两个细节
1:
在做完第一遍操作后,整个数列必须是一个不上升的数列,否则是无解的。
例如
8 10 20
这个数据就是无解的。
2:在做第二遍的时候,必须保证最左边那个数字是不能为负数的
例如数据
6 10 3
如果让10与3一起减少4的话,则3会变成-1.
后面的操作就无法进行下去的。。。。。。。负数怎么减少啊!!!
总结:
1:这个题其实是一种步步逼近的方式
2:如果发现操作的先后顺序与最终结果没有关系 ,就不妨统统从左向右做。。。类似于NOIP那个经典题均分纸牌。
#include<bits/stdc++.h> #define int long long #define isdight(c) (c>='0'&&c<='9') #define swap(a,b) a^=b^=a^=b using namespace std; int t,n,h[100005]; inline int read() //快读 { int x=0,f=1; char c=getchar(); while(!isdight(c)) f=(c^'-'?1:-1),c=getchar(); while(isdight(c)) x=(x<<1)+(x<<3)+c-'0',c=getchar(); return x*f; } int solve() { int ans=0; if(n<2) return 0; //无需改变 for(int j=1;j<=2;j++) //正序变化与倒序变化 { for(int i=2;i<n;i++) { if(h[i]>h[i-1]) { int differ=h[i]-h[i-1]; ans+=differ*2; //每次喂两袋 h[i+1]-=differ; h[i]=h[i-1]; //同时变化 } } if(h[n]>h[n-1]) return -1; //无解 if(h[n]<0) return -1; //无解 reverse(h+1,h+1+n); //反转 } return ans; } signed main() { t=read(); while(t--) { n=read(); for(int i=1;i<=n;i++) h[i]=read(); printf("%lld\n",solve()); } return 0; }