糖果传递 贪心+找规律


参考

一开始以为是差分,后来发现差分是在某一端一起+1或-1,所以这道题其实是贪心

假设有n个小朋友,每个小朋友有ai个糖果,小朋友给出去的为xi,平均数为avg
每个小朋友手上剩余的:
1:a1+x2-x1=avg;//自己手上有的ai,给出去的xi,收到的x(i+1),最后结果是avg
2:a2+x3-x2=avg;
....
n-1:an-1+xn-x(n-1)=avg;
n:an+x1-xn=avg;

我们发现,如果知道x1,那么x2,x3都可以知道。而我们要求的其实是:
|x1|+|x2|+|x3|+...+|xn-1|+|xn|的最小值;
因此只要得到xn与x1的关系,这道题就转化为了一个一元函数求极值的问题。

我们假设x1已经知道,推出xn与x1的关系:
x2=x1+avg-a1;
x3=x2+avg-a2=x1+avg-a1+avg-a2;
...
xn=xn-1+avg-an-1;
由此,我们大致可以将|x1|+|x2|+|x3|+...+|xn-1|+|xn|变为一个类似:
|x1|+|x1+?|+|x1+??|...+|x1+?????|的形式。
不过加法不如减法好做,因为我们知道:
|x1|+|x1-?|+|x1-??|+...+|x1-????|的形式的集合意义其实是x1到所有点的距离之和。
因此我们可以将原式推为减法的形式:
x2=x1-(a1-avg);
x3=x2+avg-a2=x1-(a1-avg)-(a2-avg);
...
到这里可以看出规律了!
令b1=a1-avg,b2=b1+a2-avg...
bn=bn-1+an-avg;(前缀和)
则:
x2=x1-b1;
x3=x1-b2;
...
xn=x1-bn-1;

于是原式转化为:
|x1|+|x1-b1|+...+|x1-bn-1|的最小值。
画一个数轴,最小值就是当且仅当b数列排序后,
x1为b1,b2..bn的中位数,且向下取整的时候的值。

关于为什么是中位数和向下取整:
b数组:1 2 3 4 5 6
当且仅当x1取[3,4]时,|x1-b1|+...+|x1-bn-1|都为最小。
但如果x==3,那就单独+3,肯定比+4要小~

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define mem(a,x) memset(a,x,sizeof(a));
#define db double 

//======================
const int N=1e6+10;
int a[N];ll b[N];
int n;
int main()
{
	cin>>n;
	ll sum=0,avg;
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	avg=sum/n;

	for(int i=2;i<=n;i++)
	{
		b[i]=b[i-1]+a[i]-avg;
	}
	sort(b+1,b+1+n);
	int mid=(n+1)>>1;
	int x1=b[mid];
	ll ans=0;
	for(int i=1;i<=n;i++)
	{
		ans+=abs(x1-b[i]);
	}

	cout<<ans;
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值