原题链接:糖果传递
文章目录
前言
糖果传递----其实就是一种环形均分纸牌问题,在此之前我们需要先了解,链式均分纸牌。
一、链式均分纸牌
题目描述
有N堆纸牌,编号分别为 1,2,…,N。每堆上有若干张,但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为1堆上取的纸牌,只能移到编号为2的堆上;在编号为N的堆上取的纸牌,只能移到编号为N−1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
思路:
第一堆牌相差的牌只能由第二堆牌承担(给予或索要)
第一堆牌都达到要求了又去动它干嘛
可以直接删除第一堆牌
(因为
1.已经完成目标
2.无论怎么移动都只能从第二堆里找
所以不要去管它)
第二堆牌神奇的变成了第一堆牌
重复上述思想即可解决本题。
代码实现:
#include <bits/stdc++.h>
using namespace std;
int n,avg,ans;
int a[105];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){cin>>a[i];avg+=a[i];}
avg/=n;
for(int i=1;i<=n;i++)
{
if(a[i]-avg)
{
a[i+1]+=a[i]-avg;
ans++;
}
}
cout<<ans<<endl;
system("pause");
return 0;
}
二、糖果传递(环形纸牌)
题目描述
有 n 个小朋友坐成一圈,每人有 a[i]个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为 1。求使所有人获得均等糖果的最小代价。
思路:
题目描述其实就是在说一个环里面糖怎么去传递,让每个人最后获得一样的糖且传递次数最少。
重点是推公式。
我们对于链式纸牌,第一个人只能传给第二个,所以第一个传递的纸牌数为:| T/n-a[1] |。
传出去的纸牌到了第二个,所以考虑前两个,虽然第一个人传给了第二个人,但是两人总数还是a[1]+a[2],为了让两人最后都是T/n,需要传递出去| 2*T/n-a[1]-a[2] |。
如此继续下次可以推得通式为:∑(i从1-n)∣i×T/n−G[i]∣,T为总牌数,G[i]是a[i]的前缀和,ai为编号i的牌堆的牌数,我们利用这个通式就能算出答案(对于链型)。
我们将a[i]都减去T/n变为c[i]结果显然不变,上式变为
∑(i从1-n)∣S[i]∣,
其中S[i]为c[i]前缀和。
一个重要的性质:环形纸牌必然可以拆成链式纸牌 归纳可得(由n=2,3等等。
具体证明可看这个博客。
从第k个数拆开,
原数列及其前缀和变为;
C[k+1] S[K+1]-S[K]
C[K+1] S[K+2]-S[K]
…
C[N] S[N]-S[K]
C[1] S[N]-S[K]+S[1]
C[2] S[N]-S[K]+S[2]
…
C[K] S[N]-S[K]+S[K]
因为我们减去T/n,所以最后纸牌总和为0,所以S[N]==0
所以答案变为∑(i从1-n)∣S[i]-S[k]∣。
这时,我们可以将s[i]看作数轴上的点,找一个s[k],让s[k]到各个点距离之和最小。
显然,s[k]是s数列的中位数。
证明如下:
当n为s的长度,为偶数时。
中位数有两个记为l和r。
在[l,r]之间任何一个数,确定之后,如果向左移动,那么到左边点的距离减1,
到右边点距离加1,因为是中位数,所以增减相互消除。
如果超出这个界限,那么总会有一边变化量大于另一边。
因为算距离取绝对值,所以答案会增大。所以最小距离和只能在这个区间之中。
为奇数:
中位数只有一个,所以只能取定这个数,否则无论向左取还是向右取,
都会让两边变化量不平衡,导致结果增大。
证明完毕。
最终实现代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
ll n,avg,ans;
int a[maxn],c[maxn];
int main()
{
cin>>n;
for(int i=1;i<=n;i++){cin>>a[i];avg+=a[i];}
avg/=n;
for(int i=1;i<=n;i++){c[i]=c[i-1]+a[i]-avg;}
sort(c+1,c+n+1);
ll mid=c[(n+1)/2];
for(int i=1;i<=n;i++)ans+=abs(c[i]-mid);
cout<<ans<<endl;
system("pause");
return 0;
}
最后,如果大家想做有关的题目,
推荐做七夕祭,具体思想也是环形均分纸牌,不过更加复杂。
第二篇文章,希望大家喜欢~~~