算法入门经典训练指南打卡
题目链接:UVa 11300
思路
举个栗子:设现在有四个人,第i人给上一个人的金币和收到上一个人的金币的和为xi,如图:
由上图可知:
对于A1:M = A1 - x1 + x2 --> x2 - x1 = M - A1 = -C1 --> x2 = x1 - C1
对于A2:M = A2 - x2 + x3 --> x3 - x2 = M - A2 = -C2 --> x3 = x1 - C2 - C1
对于A3:M = A3 - x3 + x4 --> x4 - x3 = M - A3 = -C3 --> x4 = x1 - C3 - C2 - C1
所以:被转手的金币总数即每个人收到的金币的总和,即x1 + x2 + ..... + xn
则对于上图:ans = |x1| + |x2| + |x3| + |x4|
= |x1| + |x1 - C1| + |x1 - C1 - C2| + |x1 - C1 - C2 - C3|
因此要想结果最小,则x1应该取C1 、C2 ......Cn的中位数
综上:只需要计算出x1的值再按照上面推导出来的式子计算出来即可
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
typedef long long LL ;
using namespace std ;
int main(){
int n ;
while (scanf("%d" , &n) == 1){
vector<LL> gold(1 , 0) ;
LL x , ans = 0 ;
for(int i = 0 ; i < n ; ++ i){
scanf("%lld" , &x) ;
gold.push_back(x + gold.back()) ; //存储前缀和
}
x = gold.back() / n ; //将平均数(即最终每个人拥有的金币数)赋值给 x
for(int i = 1 ; i <=n ; ++ i)
gold[i] -= x * i ; //计算C1 + ..... + Ci
gold.erase(gold.begin() + 0) ; //删除掉头部方便计算前缀和的无效数据 0
sort(gold.begin() , gold.end()) ; //排序
x = gold[(gold.size() + 1) / 2] ; //找到中位数
for(auto i : gold)
ans += abs(x - i) ; //计算|x1 - C1 - ...... - Cn|
printf("%lld\n" , ans) ;
}
return 0 ;
}