题目链接:https://cn.vjudge.net/problem/UVA-11300
题目大意:圆桌旁坐着n个人,每人有一定数量的金币,金币总数能被n整除。每个人可以给他左右相邻的人一些金币,最终使得每个人的金币数目相等。你的任务是求出被转手的金币,n=4,且4个人的金币数量分别为1,2,5,4时只需转移4枚金币(第数量的最小值。比如3个人给第2个人两枚金币,第2个人和第4个人分别给第1个人1枚金币)即可实现每人手中的金币数目相等。
题目思维:
初看很懵逼,后来发现是一道典型的思维题,可以作为模板的那种~~~
我们看每个人金币给或收状态
这道题目看起来很复杂,让我们慢慢分析。首先,最终每个人的金币数量可以计算出来,它等于金币总数除以人数n。接下来我们用M来表示每人最终拥有的金币数。假设有4个人,按顺序编号为1,2,3,4。假设1号给2号3枚金币,然后2号又给1号5枚金币,这实际上等价于2号给1号2枚金币,而1号什么也没给2号。这样,可以设x2表示2号给了1号多少个金币。如果x2<0,说明实际上是1号给了2号-x2枚金币。x1,x3和x4的含义类似。注意,由于是环形,x1的是1号给4号多少金币。
现在假设编号为i的人初始有A枚金币。对于1号来说,他给了4号x1枚金币,还剩A1-x1枚;但因为2号给了他x2枚金币,所以最后还剩A1-x1+x2枚金币。根据题设,该金币数等于M。换句话说,我们得到了一个方程:A1-x1+x2=M。
同理,对于第2个人,有A2-x2+x3=M。最终,我们可以得到n个方程,一共有n个变量,是不是可以直接解方程组了呢?很可惜,还不行。因为从前n-1个方程可以推导出最后一个方程(想一想,为什么)。所以,实际上只有n-1个方程是有用的。
尽管无法直接解出答案,我们还是可以尝试着用x1表示出其他的x1,则本题就变成了单变量的极值问题。
对于第1个人,A1-x1+x2=M→x2=M-A1x1=x1-C1(规定C1=A1-M,下面类似)对于第2个人,A2-x2+x3=M→x3=m-A2+x2=2M-A1-A2+x1=x1-C2
对于第3个人,A3-x3+x4=M→x4=M-A3+x=3m-a1-a2-A3+x1=x1-C3
ans为所有xi的绝对值之和
ans=x1+x2+x3+...+xn=|x1|+|x1-C1|+|x1-C2|+...+|x1-C(n-1)|
C1=A1-M
C2=A1-M+A2-M=C1+A2-M
...
分析:|x1-Ct|这个,可以看成是x1到Ct的距离
所以问题变成了:给定数轴上的n个点,找出一个点x1,使他们的距离之和最小
直接给出结论:x1为这些数(C1,C2,C3...)的中位数时,他们的距离之和最小,而xt=x1-C(t-1),所以xt应该为排序后的C(n/2)
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 #define maxn 1000005 5 int a[maxn]; 6 long long C[maxn]; 7 int main() 8 { 9 int n; 10 while(~scanf("%d",&n)) 11 { 12 long long num=0; 13 for(int i=0;i<n;i++) 14 { 15 scanf("%d",&a[i]); 16 num+=a[i]; 17 } 18 int M=num/n; 19 C[0]=0; 20 for(int i=1;i<n;i++) 21 C[i]=C[i-1]+a[i]-M; 22 sort(C,C+n); 23 int t=C[n/2]; 24 long long ans=0; 25 for(int i=0;i<n;i++) 26 ans+=abs(t-C[i]); 27 cout<<ans<<endl; 28 } 29 }