先看题目:
有n个人围成一圈,每个人有一定数量的金币,每个人可以给和他相邻的人一些金币,求挪动的最少金币使他们平分金币
这就是大意。
是不是很简单。
的确是没思路。我太难了。
只能借鉴别人的方法。
这道题其实是一道数学题。
究竟如何做呢?
下面是方法。
先把我看出来的东西展示一下。
首先,
最终剩余的金币数是相同的,而且这个金币数我们可以直接算出来,我们设最终每个人都有的金币数为m,人数为n
我们开一个变量ans记录n个人的金币数之和,那么m=ans/n。
这是大部分人能想到的。
然后重点来了:
我们要充分发挥自己的创造力,胡编乱造开始:
我们设b[i]为第i个人给第i-1个人的金币数。
那么这个有啥用?
列几个式子:
对于第一个人,他给了第n个人b[1]个金币,同时,第二个人又给了他b[2]个金币,他的初始金币数为a[1],最终金币数为m
所以我们可以列出式子 m=a[1]-b[1]+b[2] -------- 1式
同样,对于第二个人 m=a[2]-b[2]+b[3] -------- 2式
对于第n个人:m=a[n]-b[n]+b[1] -------- n式
所以m=a[n]-b[n]+b[n+1]。
但是到了这里,我们仍然看不出来有啥用,别着急,慢慢来。
接着胡编乱造
为了找规律方便,我们引入一个数组c,令c[1]=a[1]-m,c[2]=a[1]+a[2]-m*2,c[n]=a[1]+a[2]+...+a[n]-m*n
c[i]的含义并不重要,因为这是胡编乱造出来的,我也不知道,但是好用就行。
通式为c[0]=0,c[i]=a[i]+c[i-1]-m;
对于第二个人(由1式得):b[2]=m-a[1]+b[1]=b[1]-c[1];
对于第三个人(由2式得):b[3]=m-a[2]+b[2]=m-a[2]+m-a[1]+b[1](将b[2]=m-a[1]+b[1]代入)=2m-a[1]-a[2]+b[1]=b[1]-c[2];
对于第四个人(由3式得):b[4]=m-a[3]+b[3]=3m-a[1]-a[2]-a[3]+b[1]=b[1]-c[3]
……
规律出来了没有?哈哈哈哈哈哈!
我们希望所有的b[i]的绝对值之和尽量小,也就是使
|b[1]-c[0]|+|b[1]-c[1]|+|b[1]-c[2]|+|b[1]-c[3]|+……+|b[1]-c[n-1]|最小
对于任意两个数a、b,我们知道|a-b|的绝对值表示它们在数轴上的距离
所以原式我们可以看成坐标为b[1]的点到以上所有点距离之和的最小值
这时,就有一个结论,给定数轴上的n个点,在数轴上所有点中,中位数离所有顶点的距离之和最小
所以大家是不是会了。
记得要开long long哦
下面是代码:
1 #include<cmath> 2 #include<algorithm> 3 #include<cstdio> 4 #include<iostream> 5 #include<cstring> 6 using namespace std; 7 const int maxn=1e6+5; 8 typedef long long ll; 9 ll a[maxn],c[maxn],n; 10 int main(){ 11 //freopen("a.in","r",stdin); 12 while(scanf("%d",&n)!=EOF){ 13 memset(a,0,sizeof(a));memset(c,0,sizeof(c)); 14 ll tot=0; 15 for(ll i=1;i<=n;i++){ 16 scanf("%lld",&a[i]); 17 tot+=a[i]; 18 } 19 ll inin=tot/n; 20 for(ll i=1;i<n;i++){ 21 c[i]=a[i]+c[i-1]-inin; 22 } 23 sort(c,c+n); 24 ll ans=c[n/2];ll cnt=0; 25 for(ll i=0;i<n;i++){ 26 cnt+=abs(ans-c[i]); 27 } 28 printf("%lld ",cnt); 29 } 30 return 0; 31 }
总结一下吧
这道题与其说是数学题,不如说是看谁更能胡扯(当然要有理有据)
完全比拼创造力和做题经验呀!
大家可以积累一下。